mbed-os/connectivity/nanostack/sal-stack-nanostack/source/Security/PANA/pana_server.c

1766 lines
60 KiB
C

/*
* Copyright (c) 2017-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 "ns_types.h"
#include "eventOS_event.h"
#include "ns_trace.h"
#include "string.h"
#include "randLIB.h"
#include "nsdynmemLIB.h"
#include "Core/include/ns_socket.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "ccmLIB.h"
#include "shalib.h"
#include "6LoWPAN/Bootstraps/protocol_6lowpan.h"
#include "6LoWPAN/Bootstraps/protocol_6lowpan_bootstrap.h"
#ifdef ECC
#include "libX509_V3.h"
#include "ecc.h"
#endif
#include "Security/TLS/tls_lib.h"
#include "Security/Common/sec_lib.h"
#include "net_nvm_api.h"
#include "Security/PANA/pana.h"
#include "Security/PANA/pana_internal_api.h"
#include "6LoWPAN/MAC/mac_helper.h"
#include "6LoWPAN/MAC/mac_data_poll.h"
#include "6LoWPAN/ND/nd_router_object.h"
#include "Common_Protocols/udp.h"
#ifdef ECC
#include "ecc.h"
#endif
#include "common_functions.h"
#include "Security/PANA/pana_nvm.h"
#include "Security/PANA/pana_avp.h"
#include "Security/PANA/pana_eap_header.h"
#include "Security/PANA/pana_header.h"
#include "Security/PANA/eap_protocol.h"
#include "net_pana_parameters_api.h"
#include "Service_Libs/mle_service/mle_service_api.h"
#include "6LoWPAN/NVM/nwk_nvm.h"
#ifdef PANA
#ifdef PANA_SERVER_API
#define TRACE_GROUP "PanS"
static pana_server_base_t *NS_LARGE pana_server_base = NULL;
static pana_server_update_cb *pana_nvm_storage_cb = NULL;
static pana_server_session_get_cb *pana_server_nvm_get = NULL;
static pana_server_session_get_by_id_cb *pana_server_nvm__session_get = NULL;
static void (*pana_key_update_process_ready)(void) = 0;
static uint8_t *pana_nvm_buffer = 0;
static void pana_server_packet_handler(buffer_t *buf);
static int8_t pana_server_init(int8_t nwk_id, const uint8_t *network_key_material, uint8_t supported_chipher_suites, uint32_t key_update_delay);
static pana_server_base_t *pana_server_staructure_allocate(void);
static pana_key_material_t *pana_server_key_get(bool primary);
static void pana_server_set_random_key_value(uint8_t *network_key_material);
static void pana_server_material_read(const uint8_t *ptr);
static void pana_server_material_write(uint8_t *ptr);
static void pana_server_client_save_to_nvm(sec_suite_t *suite, pana_nvm_update_process_t nvm_event);
static uint8_t *pana_avp_zip_key_material(uint8_t *dptr, uint8_t *key_info, uint32_t message_seq, sec_suite_t *suite);
static void pana_client_session_init(sec_suite_t *suite);
static bool pana_auth_msg_validate(uint8_t *ptr, uint16_t length, uint8_t *key);
static void pana_pna_response_build(buffer_t *buf, uint8_t operation, pana_header_t *header, sec_suite_t *suite);
static void pana_client_authentication_fail(sec_suite_t *suite);
static void pana_key_update_delivery_ready(void);
static void pana_server_build_key_push(buffer_t *buf, sec_suite_t *suite);
static void pana_success_server_build(buffer_t *buf, sec_suite_t *suite);
static void pana_server_state_machine_func(sec_suite_t *suite);
static uint8_t *pana_avp_zip_key_material(uint8_t *dptr, uint8_t *key_info, uint32_t message_seq, sec_suite_t *suite)
{
//SET AVP BASE
dptr = pana_avp_base_write(AVP_ENCRYPT_ALGORITHM_CODE, 32, dptr, 0, 0);
uint8_t *ptr = dptr;
dptr = pana_avp_vendor_id_write_n_bytes(PANA_EAP_KEYWRAP_TYPE, 18, key_info, dptr, ZIGBEE_VENDOR_ID);
//Crypt Key Info
pana_ccm_data_crypt(ptr, 32, AES_CCM_ENCRYPT, message_seq, suite);
return dptr;
}
static pana_server_base_t *pana_server_staructure_allocate(void)
{
pana_server_base_t *server = ns_dyn_mem_alloc(sizeof(pana_server_base_t));
if (server) {
memset(server, 0, sizeof(pana_server_base_t));
}
return server;
}
static pana_key_material_t *pana_server_key_get(bool primary)
{
if (primary) {
return &pana_server_base->sec_key_material[pana_server_base->primary_material];
}
if (pana_server_base->primary_material) {
return &pana_server_base->sec_key_material[0];
}
return &pana_server_base->sec_key_material[1];
}
static void pana_server_set_random_key_value(uint8_t *network_key_material)
{
randLIB_get_n_bytes_random(network_key_material, 16);
}
static void pana_server_material_write(uint8_t *ptr)
{
pana_key_material_t *key_ptr;
ptr = common_write_32_bit(pana_server_base->pana_key_id, ptr);
ptr = common_write_32_bit(pana_server_base->session_lifetime, ptr);
ptr = common_write_32_bit(pana_server_base->key_wrap, ptr);
ptr = common_write_32_bit(pana_server_base->prf_algorythm, ptr);
ptr = common_write_32_bit(pana_server_base->integrity_algorythm, ptr);
ptr = common_write_32_bit(pana_server_base->next_session_id, ptr);
ptr = common_write_32_bit(pana_server_base->key_update_delay, ptr);
ptr = common_write_32_bit(pana_server_base->pana_key_update_delay_used, ptr); //32
*ptr++ = pana_server_base->supported_chipher_suites;
*ptr++ = pana_server_base->auth_cnt;
*ptr++ = pana_server_base->primary_material;
key_ptr = &pana_server_base->sec_key_material[0];
*ptr++ = key_ptr->key_id;
memcpy(ptr, key_ptr->key_material, 16);
ptr += 16;//52
key_ptr = &pana_server_base->sec_key_material[1];
*ptr++ = key_ptr->key_id;
memcpy(ptr, key_ptr->key_material, 16);
ptr += 16; //69
if (pana_server_base->pana_key_update) {
pana_key_update_t *pku = pana_server_base->pana_key_update;
*ptr++ = 1;
*ptr++ = pku->key_id;
ptr = common_write_16_bit(pku->key_delivery_cnt, ptr); //73
memcpy(ptr, pku->new_key_material, 16); // 89
} else {
*ptr = 0; //70
}
}
static void pana_server_material_read(const uint8_t *ptr)
{
pana_key_material_t *key_ptr;
//Set Stored setup
pana_server_base->pana_key_id = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->session_lifetime = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->key_wrap = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->prf_algorythm = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->integrity_algorythm = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->next_session_id = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->key_update_delay = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->pana_key_update_delay_used = common_read_32_bit(ptr);
ptr += 4;
pana_server_base->supported_chipher_suites = *ptr++;
pana_server_base->auth_cnt = *ptr++;
pana_server_base->primary_material = *ptr++;
key_ptr = &pana_server_base->sec_key_material[0];
key_ptr->key_id = *ptr++;
memcpy(key_ptr->key_material, ptr, 16);
tr_debug("KID0: %02x, %s", key_ptr->key_id, trace_array(ptr, 16));
ptr += 16;
key_ptr = &pana_server_base->sec_key_material[1];
key_ptr->key_id = *ptr++;
memcpy(key_ptr->key_material, ptr, 16);
tr_debug("KID1: %02x, %s", key_ptr->key_id, trace_array(ptr, 16));
ptr += 16;
uint8_t key_update = *ptr++;
if (key_update) {
pana_key_update_t *pku = ns_dyn_mem_temporary_alloc(sizeof(pana_key_update_t));
if (pku) {
pku->key_id = *ptr++;
pku->key_delivery_cnt = common_read_16_bit(ptr);
ptr += 2;
memcpy(pku->new_key_material, ptr, 16);
pana_server_base->pana_key_update = pku;
tr_debug("Key Update Process active");
}
}
}
static int8_t pana_server_init(int8_t nwk_id, const uint8_t *network_key_material, uint8_t supported_chipher_suites, uint32_t key_update_delay)
{
if (!pana_socket_init(pana_server_packet_handler, pana_server_state_machine_func, tls_server_up)) {
return -1;
}
int8_t ret_val = -1;
sec_suite_list_clean();
//Allocate
if (pana_server_base == 0) {
pana_server_base = pana_server_staructure_allocate();
} else {
if (pana_server_base->network_interface_id != nwk_id) {
tr_debug("UnKnow id");
return -2;
}
}
if (pana_server_base) {
pana_server_base->primary_material = 0;
pana_key_material_t *key_info = pana_server_key_get(false);
key_info->key_id = 0;
key_info = pana_server_key_get(true);
key_info->key_id = 1;
if (network_key_material) {
memcpy(key_info->key_material, network_key_material, 16);
} else {
pana_server_set_random_key_value(key_info->key_material);
}
pana_server_base->auth_cnt = 0;
pana_server_base->pana_key_id = 1;
pana_server_base->session_lifetime = 0xffffffff;
pana_server_base->key_wrap = 1;
pana_server_base->prf_algorythm = 5;
pana_server_base->integrity_algorythm = 0x0c;
pana_server_base->next_session_id = 1;
pana_server_base->key_update_delay = 0;
pana_server_base->supported_chipher_suites = supported_chipher_suites;
pana_server_base->pana_key_update = 0;
pana_server_base->open_pana_authentication_cnt = 0;
pana_server_base->network_interface_id = nwk_id;
pana_server_base->pana_key_update_delay_used = key_update_delay;
if (pana_nvm_storage_cb) {
pana_server_material_write(pana_nvm_buffer);
pana_nvm_storage_cb(PANA_SERVER_MATERIAL_UPDATE);
}
ret_val = 0;
}
return ret_val;
}
void pana_key_update_delivery_ready(void)
{
if (pana_server_base && pana_server_base->pana_key_update) {
if (pana_server_base->pana_key_update->key_delivery_cnt-- == 1) {
if (pana_key_update_process_ready) {
pana_server_base->key_update_delay = 1;
} else {
pana_server_base->key_update_delay = pana_server_base->pana_key_update_delay_used;
if (pana_server_base->key_update_delay == 0) {
tr_debug("Delivery Ready");
pana_server_base->key_update_delay = 1;
}
}
//NVM UPDAte for timer and key material
}
if (pana_nvm_storage_cb) {
pana_server_material_write(pana_nvm_buffer);
pana_nvm_storage_cb(PANA_SERVER_MATERIAL_UPDATE);
}
}
}
/* Save and load stored settings to Pana server core */
static void pana_server_client_save_to_nvm(sec_suite_t *suite, pana_nvm_update_process_t nvm_event)
{
uint8_t *ptr = pana_nvm_buffer;
pana_session_t *p_session = &suite->pana_session;
/* static Part of every session */
ptr = common_write_16_bit(p_session->nvm_offset, ptr);
memcpy(ptr, suite->session_address, 16);
ptr += 16;
ptr = common_write_16_bit(suite->session_port, ptr);
ptr = common_write_32_bit(p_session->session_id, ptr);
if (nvm_event == PANA_SERVER_CLIENT_SESSION_REMOVE_UPDATE) {
tr_debug("Remove From NVM");
} else {
/* First Encrypted sector start */
*ptr++ = suite->state;
ptr = common_write_32_bit(suite->timer, ptr);
uint8_t state = 0;
if (p_session->key_warp) {
state |= PANA_ST_KEY_WRAP;
}
if (p_session->session_ready) {
state |= PANA_ST_READY;
}
*ptr++ = state; //6
ptr = common_write_32_bit(p_session->req_seq, ptr);
ptr = common_write_32_bit(p_session->res_seq, ptr);
*ptr++ = p_session->address_status;
memcpy(ptr, p_session->session_relay_address, 16);
ptr += 16;
ptr = common_write_16_bit(p_session->relay_port, ptr); //37 bytes sector
*ptr++ = p_session->auth_cnt;
*ptr++ = p_session->nwk_key_id;
memcpy(ptr, p_session->pana_auth_key, 32);
ptr += 32;
memcpy(ptr, p_session->pana_PAA_enc_key, 16);
ptr += 16;
ptr = common_write_32_bit(p_session->pana_key_id, ptr);
ptr = common_write_32_bit(p_session->session_lifetime, ptr);
*ptr++ = p_session->eap_id_seq;
}
}
static void pana_server_client_restore_from_nvm(sec_suite_t *suite, const uint8_t *ptr)
{
pana_session_t *p_session = &suite->pana_session;
p_session->nvm_offset = common_read_16_bit(ptr);
ptr += 2;
memcpy(suite->session_address, ptr, 16);
ptr += 16;
suite->session_port = common_read_16_bit(ptr);
ptr += 2;
p_session->session_id = common_read_32_bit(ptr);
ptr += 4; //24
suite->state = (sec_state_machine_t) * ptr++;
suite->timer = common_read_32_bit(ptr);
ptr += 4;
uint8_t state = *ptr++;
p_session->user_server = true;
if (state & PANA_ST_KEY_WRAP) {
p_session->key_warp = true;
} else {
p_session->key_warp = false;
}
if (state & PANA_ST_READY) {
p_session->session_ready = true;
} else {
p_session->session_ready = false;
}
p_session->req_seq = common_read_32_bit(ptr);
ptr += 4;
p_session->res_seq = common_read_32_bit(ptr);
ptr += 4;
p_session->address_status = *ptr++;
memcpy(p_session->session_relay_address, ptr, 16);
ptr += 16;
p_session->relay_port = common_read_16_bit(ptr);
ptr += 2;
p_session->auth_cnt = *ptr++;
p_session->nwk_key_id = *ptr++;
memcpy(p_session->pana_auth_key, ptr, 32);
ptr += 32;
memcpy(p_session->pana_PAA_enc_key, ptr, 16);
ptr += 16;
p_session->pana_key_id = common_read_32_bit(ptr);
ptr += 4;
p_session->session_lifetime = common_read_32_bit(ptr);
ptr += 4;
p_session->eap_id_seq = *ptr++;
}
void pana_session_nvm_udate(sec_suite_t *suite, pana_nvm_update_process_t update_event)
{
if (pana_nvm_storage_cb) {
if (pana_server_base) {
pana_server_client_save_to_nvm(suite, update_event);
suite->pana_session.nvm_offset = pana_nvm_storage_cb(update_event);
}
}
}
static void pana_client_session_init(sec_suite_t *suite)
{
suite->pana_session.session_ready = false;
suite->pana_session.key_warp = true;
suite->pana_session.user_server = true;
suite->pana_session.session_id = pana_server_base->next_session_id; // Take Random Number
pana_server_base->next_session_id++;
suite->pana_session.req_seq = randLIB_get_32bit();
suite->pana_session.res_seq = 0;
suite->pana_session.eap_id_seq = randLIB_get_8bit();
suite->pana_session.eap_id_seq &= 63;
suite->pana_session.eap_id_seq++;
suite->pana_session.prf_algorythm = pana_server_base->prf_algorythm;
suite->pana_session.integrity_algorythm = pana_server_base->integrity_algorythm;
suite->pana_session.key_wrap = pana_server_base->key_wrap;
//init eap fragmentation
suite->pana_session.pana_key_id = pana_server_base->pana_key_id;
suite->pana_session.session_lifetime = pana_server_base->session_lifetime;
suite->supported_chipher_suites = pana_server_base->supported_chipher_suites;
//suite->setups &= ~TLS_HANSHAKE_HASH;
suite->setups = TLS_SERVER_MODE;
suite->timer = 1;
suite->state = PANA_REQUEST_TX;
suite->retry_counter = 0;
eap_fragmentation_init(suite);
suite->pana_session.pana_heap->handshake_len = 0;
suite->pana_session.pana_heap->handshake_req_offset = 0;
randLIB_get_n_bytes_random(suite->pana_session.pana_heap->agent_nonce, 16);
if (pana_nvm_storage_cb) {
pana_server_material_write(pana_nvm_buffer);
pana_nvm_storage_cb(PANA_SERVER_MATERIAL_UPDATE);
}
}
static bool pana_auth_msg_validate(uint8_t *ptr, uint16_t length, uint8_t *key)
{
pana_avp_t authency;
authency.code = AVP_AUTHENCY_CODE;
if (!pana_avp_discover(ptr, length, &authency)) {
return false;
}
if (!pana_auth_check(ptr, length, authency.avp_ptr, key)) {
tr_debug("Auth Fail");
return false;
}
return true;
}
static void pana_server_set_key_material(uint8_t *key_material, bool new_key, uint8_t auth_cnt)
{
if (new_key) {
memcpy(key_material, pana_server_base->pana_key_update->new_key_material, 16);
key_material += 16;
*key_material++ = pana_server_base->pana_key_update->key_id;
} else {
pana_key_material_t *primary_key = pana_server_key_get(true);
memcpy(key_material, primary_key->key_material, 16);
key_material += 16;
*key_material++ = primary_key->key_id;
}
*key_material = auth_cnt;
}
static void pana_pna_response_build(buffer_t *buf, uint8_t operation, pana_header_t *header, sec_suite_t *suite)
{
uint8_t *ptr;
header->type = PANA_MSG_PNA;
header->flags &= ~PANA_FLAGS_REQUEST;
buf->buf_ptr = PANA_HEADER_LENGTH;
ptr = buffer_data_pointer(buf);
if (operation == 4) {
//Leave emtpy
} else {
uint8_t key_material[18];
if (operation == 3) {
pana_server_set_key_material(key_material, true, suite->pana_session.auth_cnt);
} else {
pana_server_set_key_material(key_material, false, suite->pana_session.auth_cnt);
}
ptr = pana_avp_zip_key_material(ptr, key_material, header->seq, suite);
}
ptr = pana_avp_write_n_bytes(AVP_AUTHENCY_CODE, 16, NULL, ptr);
buffer_data_end_set(buf, ptr);
buf = build_pana_base(buf, header, suite);
if (!buf) {
return;
}
pana_auth_hash_calc(buffer_data_pointer(buf), buffer_data_length(buf), suite->pana_session.pana_auth_key);
//Encode Pana Auth
if (buf->src_sa.address[0] == 0xfe) {
pana_set_agend_address(buf, false, suite);
} else {
//Check IF need Relay
pana_set_agend_address(buf, true, suite);
}
if (suite->pana_session.address_status & 1) {
buf = pana_relay_avp_build(buf, suite);
if (buf) {
header->flags = 0;
header->type = PANA_MSG_RELAY;
header->session_id = 0;
header->seq = 0;
buf = build_pana_base(buf, header, suite);
}
}
protocol_push(buf);
}
static void pana_complete_msg_parse(buffer_t *buf, pana_header_t *header, sec_suite_t *suite)
{
uint16_t length = buffer_data_length(buf);
uint8_t *ptr = buffer_data_pointer(buf);
if (sec_check_suite_ptrs(suite) == 0) {
buffer_free(buf);
return;
}
if (header->flags & PANA_FLAGS_REQUEST) {
buffer_free(buf);
return;
}
uint32_t key_id = 0xffffffff;
bool key_id_parsed = false;
pana_avp_t avp_temp;
//Read Key id if they are coming
avp_temp.code = AVP_KEY_ID_CODE;
if (pana_avp_discover(ptr, length, &avp_temp) && avp_temp.len == 4) {
key_id = common_read_32_bit(avp_temp.avp_ptr);
key_id_parsed = true;
}
if (key_id_parsed) {
if (!pana_auth_msg_validate(ptr, length, suite->pana_session.pana_auth_key)) {
goto pana_failure;
}
if (suite->pana_session.pana_key_id != key_id) {
goto pana_failure;
}
tr_debug("Client AUTH_OK");
sec_lib_state_machine_trig(suite, PANA_READY);
if (pana_server_base) {
if (pana_server_base->open_pana_authentication_cnt) {
pana_server_base->open_pana_authentication_cnt--;
}
}
} else {
sec_lib_state_machine_trig(suite, PANA_ERROR);
}
buffer_free(buf);
return;
pana_failure:
tr_debug("Drop Key MSG");
sec_lib_state_machine_trig(suite, PANA_FAILURE); //shuold be calc
buffer_free(buf);
return;
}
static void pana_server_finnish_error_build(buffer_t *buf, sec_suite_t *suite)
{
buf->buf_ptr = PANA_HEADER_LENGTH;
uint8_t *ptr = buffer_data_pointer(buf);
//tr_debug("End Pana and EAP");
uint8_t eap_status[4];
eap_header_build(eap_status, 4, EAP_FAILURE, suite->pana_session.eap_id_seq, 0);
ptr = pana_avp_32_bit_write(AVP_RESULT_CODE, 2, ptr);
ptr = pana_avp_write_n_bytes(AVP_EAP_PAYLOAD_CODE, 4, eap_status, ptr);
buffer_data_end_set(buf, ptr);
}
static void sec_auth_ready(sec_suite_t *suite)
{
suite->timer = 0;
tr_debug("Pana:OK");
suite->pana_session.session_ready = true;
if (suite->state == PANA_READY) {
pana_session_nvm_udate(suite, PANA_SERVER_CLIENT_SESSION_UPDATE);
}
//Reset pointer
suite->pana_session.auth_info = NULL;
pana_free_dynamic_ram(suite);
sec_suite_tls_free(suite, true);
}
#ifdef ECC
static uint32_t tls_backoff_random_timer_start(void)
{
return randLIB_get_random_in_range(2, 16);
}
#endif
static void pana_server_pana_error_handler(sec_suite_t *suite)
{
sec_lib_state_machine_lock(suite, PANA_ERROR);
pana_client_authentication_fail(suite);
seclib_session_clean(suite);
}
static void pana_server_state_machine_func(sec_suite_t *suite)
{
if (!suite) {
return;
}
uint8_t general_tx = 0;
switch (suite->state) {
case PANA_ERROR:
pana_server_pana_error_handler(suite);
return;
case PANA_READY:
case PANA_RE_VALID:
case PANA_PULL_DONE:
sec_auth_ready(suite);
break;
case PRF_CALC:
case PRF_CALC2:
case TLS_ECC_CERTIFICATE_VERIFY_SIGNATURE:
case TLS_ECC_MESSAGE_VERIFY:
case TLS_ECC_CERTIFICATE_SIGNATURE_CHECK:
case TLS_ECC_GENERATE_PUBLIC_KEY:
case TLS_ECC_GENERATE_PREMASTER_SECRET:
case TLS_ECC_SIGNATURE_MESSAGE:
break;
case TLS_HELLO_RX:
case TLS_SERVER_KEY_EXCHANGE_RX:
case TLS_SERVER_WAIT_CHANGE_CHIPHERSUITE:
case TLS_CLIENT_KEY_EXCHANGE_RX:
case TLS_CHANGE_CHIPHER:
tr_debug("%02x", suite->state);
tr_debug("Timeout");
sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL);
general_tx = 1;
break;
#ifdef ECC
case TLS_ECC_GENERATE_PUBLIC_KEY_START:
sec_ecc_gen_public_key_start(suite);
break;
case TLS_ECC_MESSAGE_SERVER_VERIFY_START:
case TLS_ECC_MESSAGE_VERIFY_START:
if (suite->tls_session) {
if (suite->tls_session->tls_heap) {
int start = 0;
tls_heap_t *theap = suite->tls_session->tls_heap;
if (theap->cert_temp_buf) {
if (suite->state == TLS_ECC_MESSAGE_SERVER_VERIFY_START) {
if (theap->signature_temp_buf == 0) {
start = 1;
}
}
} else {
start = 1;
}
if (start) {
tls_server_finnish_handle_start(suite);
} else {
tr_debug("Start Certi Check");
tls_certificate_signature_verify(suite);
}
}
}
break;
case TLS_ECC_MESSAGE_VERIFY_START2:
tls_ecc_verfify_start(suite);
break;
case TLS_SERVER_ECC_PUB_KEY_GEN:
if (sec_auth_re_check(suite)) {
if (tls_server_certi_hash_copy(suite) == 0) {
suite->timer = tls_backoff_random_timer_start();
} else {
suite->setups &= ~TLS_HANSHAKE_HASH;
//PUBLIC key cal
sec_lib_state_machine_trig(suite, TLS_ECC_GENERATE_PUBLIC_KEY_START);
}
} else {
tr_debug("TLS_SERVER_ECC_PUB_KEY_GEN timeout");
sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL);
general_tx = 1;
//Alert Shuold send Here
}
break;
case TLS_ECC_CLIENT_SIGNATURE_START:
sec_ecc_client_signature_start(suite);
break;
#endif
case TLS_UPDATE_HAS_WITH_CERTIFICATE:
#ifdef ECC
if (sec_auth_re_check(suite)) {
if (tls_certi_hash_copy(suite) == 0) {
tr_warn("Server Certficate Alloc fail");
suite->timer = 4;
} else {
sec_lib_state_machine_trig(suite, TLS_ECC_CLIENT_SIGNATURE_START);
}
} else
#endif
{
sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL);
general_tx = 1;
}
break;
case TLS_TX_SERVER_KEY_EXCHANGE:
#ifdef ECC
if (sec_auth_re_check(suite)) {
bool tx_start_OK = false;
if (suite->pana_session.assy_length || suite->pana_session.frag_length || suite->pana_session.packet_delivered) {
//Build next EAP Packet
//tr_debug("TX same again fragment piece");
tx_start_OK = pana_eap_frag_re_tx(suite);
} else {
if (tls_pana_server_exchange_build(suite)) {
suite->pana_session.packet_delivered = false;
tx_start_OK = true;
}
}
if (tx_start_OK) {
pana_timeout_timer_set(suite, suite->state);
} else {
suite->timer = tls_backoff_random_timer_start();
}
} else
#endif
{
if (suite->pana_session.assy_length) {
buffer_free(suite->pana_session.eap_assy_buf);
suite->pana_session.eap_assy_buf = 0;
suite->pana_session.assy_length = 0;
suite->pana_session.assy_off_set = 0;
}
tr_debug("TLS_TX_SERVER_KEY_EXCHANGE timeout");
sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL);
general_tx = 1;
}
break;
default:
general_tx = 1;
break;
}
if (general_tx) {
if (sec_auth_re_check(suite)) {
buffer_t *buf = buffer_get(140);
if (buf) {
buf->interface = suite->interface;
suite->timer = 600;
switch (suite->state) {
case PANA_REQUEST_TX:
pana_start_message_build(buf, suite);
break;
case EAP_IDENTITY_REQ:
pana_eap_identity_build(buf, suite);
break;
case TLS_START:
pana_eap_tls_start_build(buf, suite);
break;
case TLS_SERVER_TX_SERVER_HELLO:
tls_server_hello_build(buf, suite);
pana_eap_down(buf, suite);
break;
case TLS_KEY_CHANGE:
//Print Handshake message
tls_prepare_change_chipher_spec(suite);
tls_build_client_change_chipher_suite_finnish(buf, suite);
tls_nonce_update(suite->tls_session->tls_nonce_explit);
tr_debug("Set Keys");
pana_eap_down(buf, suite);
break;
case TLS_EAP_END_PANA_VERIFY:
pana_success_server_build(buf, suite);
break;
case PANA_KEY_UPDATE:
pana_server_build_key_push(buf, suite);
break;
case PANA_FAILURE:
pana_server_finnish_error_build(buf, suite);
pana_down(buf, suite);
break;
case TLS_FINISH:
case TLS_ALERT:
eap_fragmentation_init(suite);
pana_eap_tls_finnish_build(buf, suite);
break;
case TLS_ALERT_CLOSE_FATAL:
case TLS_ALERT_INTERNAL:
case TLS_ALERT_CHIPHER_SUITE:
case TLS_ALERT_DECRYPT:
case TLS_ALERT_BAD_CERTIFICATE:
eap_fragmentation_init(suite);
suite->setups &= ~(TLS_ECC_CERTIFICATE_REQUESTED | TLS_ECC_CERTIFICATE_RECEIVED | TLS_ECC_CERTIFICATE_VERIFY);
#ifdef ECC
{
tls_heap_t *tls_heap = suite->tls_session->tls_heap;
if (tls_heap) {
tls_ecc_heap_free(tls_heap);
}
}
#endif
if (suite->state == TLS_ALERT_DECRYPT) {
tls_alert_build(buf, ALERT_BAD_RECORD);
} else if (suite->state == TLS_ALERT_CLOSE_FATAL) {
tls_alert_build(buf, ALERT_INTERNAL_ERR);
} else if (suite->state == TLS_ALERT_BAD_CERTIFICATE) {
tls_alert_build(buf, ALERT_BAD_CERTIFICATE);
} else {
tls_alert_build(buf, ALERT_INTERNAL_ERR);
}
pana_eap_down(buf, suite);
break;
default:
tr_debug("Unknown Packet. State: %x", suite->state);
buf = buffer_free(buf);
break;
}
} else {
suite->timer = 2;
}
} else {
tr_debug("Tls Auth Re TX limit Reached. State: %x", suite->state);
switch (suite->state) {
case TLS_SERVER_TX_SERVER_HELLO:
case TLS_KEY_CHANGE:
sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL);
break;
default:
if (suite->state == PANA_KEY_UPDATE) {
tr_warn("Key Delivery Fail");
pana_key_update_delivery_ready();
}
pana_server_pana_error_handler(suite);
break;
}
}
}
}
static void pana_server_tls_session_init(sec_suite_t *suite)
{
//Generate TLS SESSION
tls_session_id_genrate(suite->tls_session->tls_session_id, 4);
suite->tls_session->id_length = 4;
memset(suite->tls_session->tls_nonce_explit, 0, 8);
}
static void pana_server_session_address_set_for_relay(sec_suite_t *suite, buffer_t *buf)
{
memcpy(suite->session_address, buf->dst_sa.address, 16);
suite->session_port = buf->dst_sa.port;
suite->pana_session.address_status = 1;
//Copy Relay Address
memcpy(suite->pana_session.session_relay_address, buf->src_sa.address, 16);
suite->pana_session.relay_port = buf->src_sa.port;
memcpy(&(buf->src_sa), &(buf->dst_sa), sizeof(sockaddr_t));
}
static sec_suite_t *pana_server_session_restore(uint8_t *nvm_data, protocol_interface_info_entry_t *interface)
{
sec_suite_t *suite = sec_lib_security_session_allocate(false);
if (!suite) {
return NULL;
}
//Restore
pana_server_client_restore_from_nvm(suite, nvm_data);
//Init Satic Information At ZIP
suite->setups = TLS_SERVER_MODE;
suite->interface = interface;
suite->pana_session.key_wrap = pana_server_base->key_wrap;
suite->pana_session.prf_algorythm = pana_server_base->prf_algorythm;
suite->pana_session.integrity_algorythm = pana_server_base->integrity_algorythm;
return suite;
}
static sec_suite_t *pana_server_get_session_by_session_id(uint32_t session_id, protocol_interface_info_entry_t *interface)
{
sec_suite_t *suite = sec_suite_selected_pana_session(session_id);
if (!suite && pana_server_nvm__session_get) {
if (!pana_server_nvm__session_get(session_id)) {
return NULL;
}
suite = pana_server_session_restore(pana_nvm_buffer, interface);
}
return suite;
}
static sec_suite_t *pana_server_get_session_by_address(uint8_t *ll_address, protocol_interface_info_entry_t *interface)
{
sec_suite_t *suite = sec_suite_selected_address(ll_address);
if (!suite && pana_server_nvm_get) {
if (!pana_server_nvm_get(ll_address)) {
return NULL;
}
suite = pana_server_session_restore(pana_nvm_buffer, interface);
}
return suite;
}
static void pana_server_packet_handler(buffer_t *buf)
{
bool relay_pack = false;
sec_suite_t *suite = NULL;
pana_header_t header;
if (!pana_header_parse(buffer_data_pointer(buf), buffer_data_length(buf), &header)) {
buffer_free(buf);
return;
}
buffer_data_strip_header(buf, PANA_HEADER_LENGTH);
if (header.type == PANA_MSG_RELAY) {
/** Parse Relay */
buf = pana_relay_parse(buf);
if (buf) {
/** Parse Relayed Packet */
if (!pana_header_parse(buffer_data_pointer(buf), buffer_data_length(buf), &header)) {
buffer_free(buf);
return;
}
buffer_data_strip_header(buf, PANA_HEADER_LENGTH);
relay_pack = true;
}
if (buf == 0) {
tr_debug("Drop Pana Relay Pack");
return;
}
}
//Server will use address and session when address is LL and GP use only ID
//SERVER: if type is relay parse relay address and save
if (header.type == PANA_MSG_PNA) {
//tr_debug("PNA");
suite = pana_server_get_session_by_session_id(header.session_id, buf->interface);
if (!suite || !(header.flags & PANA_FLAGS_PING)) {
buffer_free(buf);
return;
}
if (!pana_auth_msg_validate(buffer_data_pointer(buf), buffer_data_length(buf), suite->pana_session.pana_auth_key)) {
buffer_free(buf);
return;
}
if ((header.flags & PANA_FLAGS_REQUEST) == PANA_FLAGS_RESPONSE) {
if (suite->pana_session.req_seq != header.seq) {
tr_debug("PNA RES:Drop Old seq");
buffer_free(buf);
return;
}
suite->pana_session.req_seq++;
if (suite->pana_session.session_ready) {
tr_debug("Key push OK");
if (suite->state == PANA_KEY_UPDATE) {
sec_lib_state_machine_lock(suite, PANA_READY);
tr_debug("Key Delivery OK");
//Call SEC KEY UPDATE
//tr_debug("NVM SEQ Update by Key Pull Ready");
pana_session_nvm_udate(suite, PANA_SERVER_CLIENT_SESSION_UPDATE);
pana_key_update_delivery_ready();
}
}
buffer_free(buf);
return;
} else {
if (suite->pana_session.res_seq == 0) {
suite->pana_session.res_seq = header.seq;
} else {
if ((suite->pana_session.res_seq + 1) == header.seq) {
suite->pana_session.res_seq = header.seq;
} else if (suite->pana_session.res_seq != header.seq) {
tr_debug("PNA REQ:Drop unknow Request");
buffer_free(buf);
return;
}
}
}
if (relay_pack) {
pana_server_session_address_set_for_relay(suite, buf);
} else {
if ((buf->src_sa.address[0] != 0xfe)) {
//tr_debug("SET GP & SEQ");
suite->pana_session.address_status = 0;
memcpy(suite->pana_session.session_relay_address, buf->src_sa.address, 16);
suite->pana_session.relay_port = buf->src_sa.port;
suite->session_port = buf->src_sa.port;
} else {
if (memcmp(suite->session_address, buf->src_sa.address, 16) != 0) {
tr_debug("PNA REQ:Drop By Address");
buffer_free(buf);
return;
}
//Shuold Update Relay Information
suite->pana_session.address_status = 2;
suite->session_port = buf->src_sa.port;
lowpan_neighbour_data_clean(suite->interface->id, buf->src_sa.address);
}
}
if (!suite->pana_session.session_ready) {
buffer_free(buf);
return;
}
//KEY PULL
pana_avp_t avp_temp;
//Read Resul and Key id if they are coming
avp_temp.code = PANA_EAP_KEYREQ_TYPE;
avp_temp.len = 0;
uint8_t key_delivery;
if (!pana_avp_discover(buffer_data_pointer(buf), buffer_data_length(buf), &avp_temp)) {
key_delivery = 2;
avp_temp.avp_ptr = NULL;
} else {
if (avp_temp.len != 2) {
buffer_free(buf);
return;
}
uint8_t *ptr = avp_temp.avp_ptr;
pana_key_material_t *primary_key = pana_server_key_get(true);
tr_debug("Network KEY Request: %s", trace_array(ptr, 2));
if (*ptr++ & 1) {
tr_debug("REQ bit, 1");
//if(*ptr == pana_server_base->network_key_id)
if (*ptr == primary_key->key_id) {
if (pana_server_base->pana_key_update) {
key_delivery = 3;
} else {
key_delivery = 4;
}
} else {
key_delivery = 2;
}
} else {
//if(*ptr == pana_server_base->network_key_id)
if (*ptr == primary_key->key_id) {
key_delivery = 2;
} else {
key_delivery = 4;
}
}
}
tr_debug("NVM SEQ update BY Client key Pull");
pana_pna_response_build(buf, key_delivery, &header, suite);
pana_session_nvm_udate(suite, PANA_SERVER_CLIENT_SESSION_UPDATE);
return;
} else if (header.type == PANA_MSG_PCI) {
if (relay_pack) {
suite = pana_server_get_session_by_address(buf->dst_sa.address, buf->interface);
} else {
suite = pana_server_get_session_by_address(buf->src_sa.address, buf->interface);
}
if (suite) {
//Check State
//if state ready create new session
if (!suite->pana_session.session_ready) {
tr_debug("PCI received; Discard unfinished session and start a new one");
if (pana_server_base->open_pana_authentication_cnt) {
pana_server_base->open_pana_authentication_cnt--;
}
sec_suite_remove(suite);
suite = 0;
} else {
tr_debug("Accept new session");
}
}
if (suite) {
if (suite->pana_session.pana_heap == NULL) {
suite->pana_session.pana_heap = pana_heap_structure_allocate();
}
if (suite->tls_session == NULL) {
suite->tls_session = amr_tls_session_allocate();
if (suite->tls_session == NULL) {
if (suite->pana_session.pana_heap) {
ns_dyn_mem_free(suite->pana_session.pana_heap);
suite->pana_session.pana_heap = NULL;
}
}
}
if (suite->pana_session.pana_heap) {
arm_tls_session_clear(suite->tls_session);
suite->tls_session->tls_heap = tls_heap_allocate();
if (suite->tls_session->tls_heap) {
//Generate TLS SESSION
pana_server_tls_session_init(suite);
pana_client_session_init(suite);
tr_debug("UPdate Auth Counter");
if (relay_pack) {
pana_server_session_address_set_for_relay(suite, buf);
} else {
suite->pana_session.address_status = 2;
memcpy(suite->session_address, buf->src_sa.address, 16);
suite->session_port = buf->src_sa.port;
}
pana_server_base->open_pana_authentication_cnt++;
} else {
tr_debug("TLS heap allocate");
}
}
} else {
//Generate session
suite = sec_suite_create();
if (suite) {
tr_debug("Create new Pana Session");
pana_server_tls_session_init(suite);
pana_server_base->open_pana_authentication_cnt++;
pana_client_session_init(suite);
suite->interface = buf->interface;
if (relay_pack) {
pana_server_session_address_set_for_relay(suite, buf);
} else {
suite->pana_session.address_status = 2;
memcpy(suite->session_address, buf->src_sa.address, 16);
suite->session_port = buf->src_sa.port;
}
}
}
buffer_free(buf);
return;
} else if (header.type == PANA_MSG_PA) {
if (buf->src_sa.address[0] == 0xfe || relay_pack) {
//accept only now session
suite = sec_suite_selected_pana_session(header.session_id);
if (!suite) {
tr_debug("Drop Packet by session ID");
buffer_free(buf);
return;
}
if ((header.flags & PANA_FLAGS_REQUEST) == PANA_FLAGS_RESPONSE) {
if (suite->pana_session.req_seq == header.seq) {
suite->pana_session.req_seq++;
//ADD Check for EAP fragmentation!!!!!!!!!!!!!!!
if ((header.flags & PANA_FLAGS_COMPLETE) == PANA_FLAGS_COMPLETE) {
if (suite->state != TLS_EAP_END_PANA_VERIFY && suite->state != PANA_READY) {
if (pana_server_base->open_pana_authentication_cnt) {
pana_server_base->open_pana_authentication_cnt--;
}
tr_debug("Remove Current session by 'Comp bit'");
sec_suite_remove(suite);
buffer_free(buf);
return;
}
}
} else {
tr_debug("Pana RES:Drop Old seq");
buffer_free(buf);
return;
}
} else {
suite->pana_session.res_seq = header.seq;
}
if (relay_pack) {
buf->src_sa = buf->dst_sa;
}
if (header.flags & PANA_FLAGS_START) {
pana_session_startms_parse(buf, &header, suite);
} else if (header.flags & PANA_FLAGS_COMPLETE) {
pana_complete_msg_parse(buf, &header, suite);
} else {
buf = pana_auth_message_handler(buf, &header, suite);
if (buf) {
pana_eap_tls_up(buf, suite);
}
}
return;
}
buffer_free(buf);
} else {
buffer_free(buf);
}
}
int8_t pana_server_interface_init(int8_t interface_id, net_tls_cipher_e cipher_mode, const uint8_t *key_material, uint32_t time_period_before_activate_key)
{
int8_t ret_val;
uint8_t supported_chipher_suites;
protocol_interface_info_entry_t *cur;
cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
}
if (!cur->if_lowpan_security_params) {
return -1;
}
if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) {
return -4;
} else if (cur->if_lowpan_security_params->pana_params == 0) {
return -3;
} else if (cur->if_lowpan_security_params->nwk_security_mode != NET_SEC_MODE_PANA_LINK_SECURITY) {
return -5;
}
switch (cipher_mode) {
case NET_TLS_PSK_CIPHER: /**< Network Authentication support only PSK */
supported_chipher_suites = SEC_CIPHERSUITE_PSK;
break;
case NET_TLS_ECC_CIPHER: /**< Network Authentication support only ECC */
case NET_TLS_PSK_AND_ECC_CIPHER: /**< Network Authentication support PSK & ECC */
#ifdef ECC
//Verify Certficate
if (sec_cetificate_chain_get(SEC_NWK_AUTHENTICATION_CERTI_CHAIN) == NULL) {
return -1;
}
if (cipher_mode == NET_TLS_PSK_AND_ECC_CIPHER) {
supported_chipher_suites = (SEC_CIPHERSUITE_PSK | SEC_CIPHERSUITE_ECC);
} else {
supported_chipher_suites = SEC_CIPHERSUITE_ECC;
}
break;
#endif // if ECC is not supported, flow into default => nothing is supported.
default:
supported_chipher_suites = 0;
break;
}
ret_val = pana_server_init(interface_id, key_material, supported_chipher_suites, time_period_before_activate_key);
if (ret_val == 0) {
cur->if_lowpan_security_params->pana_params->nwk_chipher_mode = cipher_mode;
cur->if_lowpan_security_params->pana_params->psk_key_id = 0;
cur->if_lowpan_security_params->pana_params->pana_client = 0;
cur->lowpan_info |= (INTERFACE_NWK_BOOTSRAP_PANA_AUTHENTICATION);
cur->configure_flags |= INTERFACE_SECURITY_DEFINED;
}
return ret_val;
}
int8_t pana_server_key_update(int8_t interface_id, const uint8_t *network_key_material)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
}
if (!pana_server_base || pana_server_base->network_interface_id != interface_id || pana_server_base->pana_key_update) {
return -2;
}
if (!cur->if_lowpan_security_params || !cur->if_lowpan_security_params->pana_params) {
return -3;
}
if ((cur->lowpan_info & INTERFACE_NWK_ACTIVE) == 0) {
return -4;
} else if (cur->if_lowpan_security_params->nwk_security_mode != NET_SEC_MODE_PANA_LINK_SECURITY) {
return -5;
}
pana_server_base->pana_key_update = ns_dyn_mem_temporary_alloc(sizeof(pana_key_update_t));
if (!pana_server_base->pana_key_update) {
return -3;
}
pana_key_material_t *primary_key = pana_server_key_get(true);
if (network_key_material) {
memcpy(pana_server_base->pana_key_update->new_key_material, network_key_material, 16);
} else {
pana_server_set_random_key_value(pana_server_base->pana_key_update->new_key_material);
network_key_material = pana_server_base->pana_key_update->new_key_material;
}
pana_lib_parameters_s *parameters = pana_parameters_get();
/* Update Key ID */
pana_server_base->pana_key_update->key_id = primary_key->key_id;
if (pana_server_base->pana_key_update->key_id == parameters->KEY_ID_MAX_VALUE) {
/* Start Lollipop Sechema from 1 again */
pana_server_base->pana_key_update->key_id = 0;
}
pana_server_base->pana_key_update->key_id++;
//Set New Key to secondary key
primary_key = pana_server_key_get(false);
primary_key->key_id = pana_server_base->pana_key_update->key_id;
memcpy(primary_key->key_material, pana_server_base->pana_key_update->new_key_material, 16);
//TRIG Timers
pana_server_base->pana_key_update->key_delivery_cnt = sec_pana_key_update_trig(parameters->KEY_UPDATE_THRESHOLD);
uint8_t *key_ptr = pana_key_get(network_key_material);
if (key_ptr) {
//pana_server_base->auth_cnt++;
tr_debug("SET Secondary Key ready");
mac_helper_security_next_key_set(cur, (key_ptr + 16), pana_server_base->pana_key_update->key_id, MAC_KEY_ID_MODE_IDX);
mle_service_security_set_security_key(cur->id, key_ptr, pana_server_base->pana_key_update->key_id, false);
}
tr_debug("KEY Delivery CNT: %02x", pana_server_base->pana_key_update->key_delivery_cnt);
if (pana_server_base->pana_key_update->key_delivery_cnt == 0) {
tr_debug("TRIG NEW Key");
pana_server_base->pana_key_update->key_delivery_cnt = 1;
pana_key_update_delivery_ready();
}
if (pana_nvm_storage_cb) {
pana_server_material_write(pana_nvm_buffer);
pana_nvm_storage_cb(PANA_SERVER_MATERIAL_UPDATE);
}
return 0;
}
void pana_key_update_delay_timer(void)
{
if (pana_server_base && pana_server_base->key_update_delay) {
if (pana_server_base->key_update_delay <= 100) {
protocol_interface_info_entry_t *cur;
pana_server_base->key_update_delay = 0;
cur = protocol_stack_interface_info_get_by_id(pana_server_base->network_interface_id);
//SWAP new KEY
if (cur) {
tr_debug("Trig New Key ID");
uint8_t keyId = mle_service_security_next_key_id_get(cur->id);
mac_helper_security_key_swap_next_to_default(cur);
mle_service_security_key_trig(cur->id, keyId);
if (cur->nwk_wpan_nvm_api) {
cur->nwk_wpan_nvm_api->nvm_params_update_cb(cur->nwk_wpan_nvm_api, true);
}
if (pana_key_update_process_ready) {
pana_key_update_process_ready();
}
}
if (pana_server_base->primary_material) {
pana_server_base->primary_material = 0;
} else {
pana_server_base->primary_material = 1;
}
pana_key_material_t *primary_key = pana_server_key_get(true);
primary_key->key_id = pana_server_base->pana_key_update->key_id;
memcpy(primary_key->key_material, pana_server_base->pana_key_update->new_key_material, 16);
if (pana_server_base->pana_key_update) {
ns_dyn_mem_free(pana_server_base->pana_key_update);
pana_server_base->pana_key_update = 0;
}
if (pana_nvm_storage_cb) {
tr_debug("PS:NVM Cover");
pana_server_material_write(pana_nvm_buffer);
pana_nvm_storage_cb(PANA_SERVER_MATERIAL_UPDATE);
}
} else {
pana_server_base->key_update_delay -= 100;
}
}
}
int8_t pana_server_nvm_callback_set(pana_server_update_cb *update_cb, pana_server_session_get_cb *nvm_get, pana_server_session_get_by_id_cb *nvm_session_get, uint8_t *nvm_static_buffer)
{
if (!update_cb || !nvm_static_buffer || !nvm_get || !nvm_session_get) {
return -1;
}
pana_nvm_storage_cb = update_cb;
pana_server_nvm_get = nvm_get;
pana_server_nvm__session_get = nvm_session_get;
pana_nvm_buffer = nvm_static_buffer;
return 0;
}
int8_t pana_server_process_ready_cb_set(void (*cb_fptr)(void))
{
if (cb_fptr) {
pana_key_update_process_ready = cb_fptr;
return 0;
}
return -1;
}
/* Pana Client session load from NVM API */
int8_t pana_server_nvm_client_session_load(uint8_t *nvm_pointer)
{
if (!pana_server_base) {
return -1;
}
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(pana_server_base->network_interface_id);
if (!cur) {
return -1;
}
if (pana_socket_id_get() == -1) {
return -2;
}
if (pana_server_session_restore(nvm_pointer, cur)) {
return -3;
}
return 0;
}
static void pana_success_server_build(buffer_t *buf, sec_suite_t *suite)
{
uint8_t *ptr;
pana_header_t header;
header.type = PANA_MSG_PA;
header.flags = PANA_FLAGS_COMPLETE | PANA_FLAGS_REQUEST;
//REQUEST
header.seq = suite->pana_session.req_seq;
header.session_id = suite->pana_session.session_id;
buf->buf_ptr = PANA_HEADER_LENGTH;
ptr = buffer_data_pointer(buf);
uint8_t eap_status[4];
eap_header_build(eap_status, 4, EAP_SUCCESS, suite->pana_session.eap_id_seq, 0);
ptr = pana_avp_32_bit_write(AVP_RESULT_CODE, 0, ptr);
ptr = pana_avp_write_n_bytes(AVP_EAP_PAYLOAD_CODE, 4, eap_status, ptr);
ptr = pana_avp_32_bit_write(AVP_KEY_ID_CODE, suite->pana_session.pana_key_id, ptr);
ptr = pana_avp_32_bit_write(AVP_SESSION_LIFETIME_CODE, suite->pana_session.session_lifetime, ptr);
//ENC
uint8_t key_material[18];
pana_server_set_key_material(key_material, false, suite->pana_session.auth_cnt);
ptr = pana_avp_zip_key_material(ptr, key_material, header.seq, suite);
ptr = pana_avp_write_n_bytes(AVP_AUTHENCY_CODE, 16, NULL, ptr);
buffer_data_end_set(buf, ptr);
buf = build_pana_base(buf, &header, suite);
if (!buf) {
return;
}
pana_auth_hash_calc(buffer_data_pointer(buf), buffer_data_length(buf), suite->pana_session.pana_auth_key);
//Encode Pana Auth
pana_set_agend_address(buf, false, suite);
if (suite->pana_session.address_status & 1) {
buf = pana_relay_avp_build(buf, suite);
if (buf) {
header.flags = 0;
header.type = PANA_MSG_RELAY;
header.session_id = 0;
header.seq = 0;
buf = build_pana_base(buf, &header, suite);
}
}
protocol_push(buf);
}
static void pana_server_build_key_push(buffer_t *buf, sec_suite_t *suite)
{
uint8_t *ptr;
pana_header_t header;
header.type = PANA_MSG_PNA;
header.flags = PANA_FLAGS_REQUEST | PANA_FLAGS_PING;
//REQUEST
header.seq = suite->pana_session.req_seq;
header.session_id = suite->pana_session.session_id;
buf->buf_ptr = PANA_HEADER_LENGTH;
ptr = buffer_data_pointer(buf);
//ENC
uint8_t key_material[18];
pana_server_set_key_material(key_material, true, suite->pana_session.auth_cnt);
ptr = pana_avp_zip_key_material(ptr, key_material, header.seq, suite);
ptr = pana_avp_write_n_bytes(AVP_AUTHENCY_CODE, 16, NULL, ptr);
buffer_data_end_set(buf, ptr);
buf = build_pana_base(buf, &header, suite);
if (!buf) {
return;
}
pana_auth_hash_calc(buffer_data_pointer(buf), buffer_data_length(buf), suite->pana_session.pana_auth_key);
pana_set_agend_address(buf, true, suite);
protocol_push(buf);
}
int8_t pana_network_key_get(int8_t interface_id, ns_keys_t *key)
{
(void)interface_id;
if (pana_server_base && key) {
pana_key_material_t *primary_key = pana_server_key_get(true);
pana_key_material_t *prev_key = pana_server_key_get(false);
if (prev_key->key_id == 0) {
prev_key = 0;
}
key->current_active_key_index = primary_key->key_id;
memcpy(key->current_active_network_key, primary_key->key_material, 16);
if (prev_key) {
key->previous_active_key_index = prev_key->key_id;
memcpy(key->previous_active_network_key, prev_key->key_material, 16);
} else {
memset(key->previous_active_network_key, 0, 16);
}
return 0;
}
return -1;
}
int8_t pana_server_trig_new_key(int8_t interface_id)
{
if (!pana_server_base) {
return -1;
}
if (pana_server_base->network_interface_id != interface_id) {
return -2;
}
if (pana_server_base->key_update_delay) {
tr_debug("TRIG faster");
pana_server_base->key_update_delay = 1;
return 0;
}
return -1;
}
int8_t pana_server_restore_from_nvm(uint8_t *nvm_data, int8_t interface_id)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
}
if (!pana_socket_init(pana_server_packet_handler, pana_server_state_machine_func, tls_server_up)) {
return -1;
}
sec_suite_list_clean();
//Allocate
if (pana_server_base == 0) {
pana_server_base = pana_server_staructure_allocate();
}
if (!pana_server_base || !cur->if_lowpan_security_params || !cur->if_lowpan_security_params->pana_params) {
return -1;
}
net_tls_cipher_e cipher_mode;
pana_server_base->open_pana_authentication_cnt = 0;
pana_server_base->network_interface_id = interface_id;
pana_server_base->pana_key_update = 0;
pana_server_base->key_update_delay = 0;
pana_server_material_read(nvm_data);
if (pana_server_base->supported_chipher_suites == SEC_CIPHERSUITE_PSK) {
cipher_mode = NET_TLS_PSK_CIPHER;
} else if (pana_server_base->supported_chipher_suites == SEC_CIPHERSUITE_ECC) {
cipher_mode = NET_TLS_ECC_CIPHER;
} else {
cipher_mode = NET_TLS_PSK_AND_ECC_CIPHER;
}
cur->if_lowpan_security_params->pana_params->nwk_chipher_mode = cipher_mode;
cur->if_lowpan_security_params->pana_params->psk_key_id = 0;//TODO?????
cur->if_lowpan_security_params->pana_params->pana_client = 0;
cur->lowpan_info |= (INTERFACE_NWK_BOOTSRAP_PANA_AUTHENTICATION);
cur->configure_flags |= INTERFACE_SECURITY_DEFINED;
return 0;
}
int8_t pana_server_key_material_load(int8_t interface_id)
{
protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
if (!cur) {
return -1;
} else if (!pana_server_base) {
return -1;
}
uint8_t *key_ptr;
pana_key_material_t *key_mat;
if (pana_server_base->pana_key_update) {
pana_server_base->auth_cnt++;
//Load First Primary to master
key_mat = pana_server_key_get(true);
key_ptr = pana_key_get(key_mat->key_material);
mac_helper_security_default_key_set(cur, (key_ptr + 16), key_mat->key_id, MAC_KEY_ID_MODE_IDX);
mle_service_security_set_security_key(cur->id, key_ptr, key_mat->key_id, true);
if (cur->nwk_wpan_nvm_api) {
cur->nwk_wpan_nvm_api->nvm_params_update_cb(cur->nwk_wpan_nvm_api, true);
}
//Set Secondary to coming key
key_mat = pana_server_key_get(false);
key_ptr = pana_key_get(key_mat->key_material);
mac_helper_security_next_key_set(cur, (key_ptr + 16), key_mat->key_id, MAC_KEY_ID_MODE_IDX);
mle_service_security_set_security_key(cur->id, key_ptr, key_mat->key_id, false);
} else {
bool secondary_active = false;
//Load first Secondary and then Primary
key_mat = pana_server_key_get(false);
if (key_mat->key_id) {
key_ptr = pana_key_get(key_mat->key_material);
secondary_active = true;
mac_helper_security_default_key_set(cur, (key_ptr + 16), key_mat->key_id, MAC_KEY_ID_MODE_IDX);
mle_service_security_set_security_key(cur->id, key_ptr, key_mat->key_id, true);
if (cur->nwk_wpan_nvm_api) {
cur->nwk_wpan_nvm_api->nvm_params_update_cb(cur->nwk_wpan_nvm_api, true);
}
}
key_mat = pana_server_key_get(true);
key_ptr = pana_key_get(key_mat->key_material);
if (secondary_active) {
mac_helper_security_next_key_set(cur, (key_ptr + 16), key_mat->key_id, MAC_KEY_ID_MODE_IDX);
mle_service_security_set_security_key(cur->id, key_ptr, key_mat->key_id, false);
mac_helper_security_key_swap_next_to_default(cur);
mle_service_security_key_trig(cur->id, key_mat->key_id);
} else {
mac_helper_security_default_key_set(cur, (key_ptr + 16), key_mat->key_id, MAC_KEY_ID_MODE_IDX);
mle_service_security_set_security_key(cur->id, key_ptr, key_mat->key_id, true);
}
if (cur->nwk_wpan_nvm_api) {
cur->nwk_wpan_nvm_api->nvm_params_update_cb(cur->nwk_wpan_nvm_api, true);
}
}
return 0;
}
static void pana_client_authentication_fail(sec_suite_t *suite)
{
if (suite->pana_session.nvm_offset) {
//tr_debug("NVM Session Remove");
pana_session_nvm_udate(suite, PANA_SERVER_CLIENT_SESSION_REMOVE_UPDATE);
}
if (pana_server_base && pana_server_base->open_pana_authentication_cnt) {
pana_server_base->open_pana_authentication_cnt--;
}
}
#else
int8_t pana_server_nvm_callback_set(pana_server_update_cb *update_cb, pana_server_session_get_cb *nvm_get, pana_server_session_get_by_id_cb *nvm_session_get, uint8_t *nvm_static_buffer)
{
(void)update_cb;
(void)nvm_get;
(void)nvm_session_get;
(void)nvm_static_buffer;
return -1;
}
int8_t pana_server_nvm_client_session_load(uint8_t *nvm_pointer)
{
(void) nvm_pointer;
return -1;
}
int8_t pana_server_restore_from_nvm(uint8_t *nvm_data, int8_t interface_id)
{
(void) nvm_data;
(void)interface_id;
return -1;
}
#endif
#else
int8_t pana_server_nvm_callback_set(pana_server_update_cb *update_cb, pana_server_session_get_cb *nvm_get, pana_server_session_get_by_id_cb *nvm_session_get, uint8_t *nvm_static_buffer)
{
(void)update_cb;
(void)nvm_get;
(void)nvm_session_get;
(void)nvm_static_buffer;
return -1;
}
int8_t pana_server_nvm_client_session_load(uint8_t *nvm_pointer)
{
(void) nvm_pointer;
return -1;
}
int8_t pana_server_restore_from_nvm(uint8_t *nvm_data, int8_t interface_id)
{
(void) nvm_data;
(void)interface_id;
return -1;
}
#endif