/* * Copyright (c) 2017-2019, 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 "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 #define TRACE_GROUP "PanC" static pana_client_session_update_cb *pana_client_nvm_storage_cb = NULL; static pana_client_session_get_cb *pana_client_session_get = NULL; static uint8_t *pana_client_nvm_buffer = 0; static void pana_complete_msg_parse(buffer_t *buf, pana_header_t *header, sec_suite_t *suite); static void pana_client_packet_handler(buffer_t *buf); static buffer_t *pana_auth_msg_build(buffer_t *buf, pana_header_t *header, sec_suite_t *suite); static void pana_client_pna_handler(buffer_t *buf, pana_header_t *header, sec_suite_t *suite); static bool pana_check_address(buffer_t *buf); static uint8_t *pana_avp_zip_key_req(uint8_t *dptr, protocol_interface_info_entry_t *cur); static void pana_client_session_nvm_udate(sec_suite_t *suite); static void pana_session_data_load_to_session(uint8_t *data_buf, sec_suite_t *suite); static uint8_t pana_ping_notify_msg_generate(uint8_t key_req, sec_suite_t *suite); static uint8_t *pana_avp_zip_key_req(uint8_t *dptr, protocol_interface_info_entry_t *cur) { uint8_t key_req[2]; key_req[0] = 1; key_req[1] = mac_helper_default_key_index_get(cur); return pana_avp_vendor_id_write_n_bytes(PANA_EAP_KEYREQ_TYPE, 2, key_req, dptr, ZIGBEE_VENDOR_ID); } bool pana_check_address(buffer_t *buf) { if (buf->src_sa.address[0] == 0xfe && buf->dst_sa.address[0] == 0xfe) { return true; } return false; } static buffer_t *pana_auth_msg_build(buffer_t *buf, pana_header_t *header, sec_suite_t *suite) { uint8_t *ptr; buf->buf_ptr = PANA_HEADER_LENGTH; ptr = buffer_data_pointer(buf); ptr = pana_avp_32_bit_write(AVP_KEY_ID_CODE, suite->pana_session.pana_key_id, ptr); ptr = pana_avp_write_n_bytes(AVP_AUTHENCY_CODE, 16, NULL, ptr); buffer_data_end_set(buf, ptr); header->flags = PANA_FLAGS_COMPLETE; return build_pana_base(buf, header, suite); } static uint8_t *pana_client_keywrap_parse(uint8_t *ptr, uint16_t len, sec_suite_t *suite) { suite->pana_session.key_warp = true; pana_avp_t avp_data; avp_data.code = PANA_EAP_KEYWRAP_TYPE; if (!pana_avp_discover(ptr, len, &avp_data) || avp_data.len != 18) { return NULL; } if (avp_data.flags & PANA_EAP_VENDOR_FLAG) { if (avp_data.vendor_id != ZIGBEE_VENDOR_ID) { tr_debug("Vendor not ZIP: %02"PRIu32, avp_data.vendor_id); return NULL; } } tr_debug("Network Key id: %02x, Ctr: %02x", avp_data.avp_ptr[16], avp_data.avp_ptr[17]); return avp_data.avp_ptr; } static bool pana_message_authency_validate(uint8_t *ptr, uint16_t length, uint8_t *auth_key) { pana_avp_t avp_temp; avp_temp.code = AVP_AUTHENCY_CODE; if (!pana_avp_discover(ptr, length, &avp_temp)) { return false; } return pana_auth_check(ptr, length, avp_temp.avp_ptr, auth_key); } 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; uint32_t result = 0; //Default if not sended pana_avp_t avp_temp; //Read Resul and Key id if they are coming avp_temp.code = AVP_RESULT_CODE; if (pana_avp_discover(ptr, length, &avp_temp) && avp_temp.len == 4) { result = common_read_32_bit(avp_temp.avp_ptr); } 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; } uint32_t lifetime = 0xffffffff; tr_debug("Handle Compelete request"); if ((header->agent_retry) && suite->state == PANA_READY) { if (!pana_message_authency_validate(ptr, length, suite->pana_session.pana_auth_key)) { buffer_free(buf); return; } tr_debug("RE TX response for Pana Complete"); goto build_response; } bool eap_status = true; avp_temp.code = AVP_EAP_PAYLOAD_CODE; if (pana_avp_discover(ptr, length, &avp_temp)) { eap_header_t eap_header; if (eap_header_parse(avp_temp.avp_ptr, avp_temp.len, &eap_header)) { if (eap_header.eap_code == EAP_SUCCESS) { tr_debug("EAP success"); } else if (eap_header.eap_code == EAP_FAILURE) { tr_debug("EAP Failure"); eap_status = false; } } } //Validate Result if (result || !eap_status) { tr_debug("PANA / EAP Failure Result"); //Check State if (suite->state != PANA_PING_REQ) { goto pana_failure; } tr_debug("Reset Pana for new session"); suite->pana_session.key_warp = false; suite->pana_session.session_ready = false; pana_session_init_by_session_ptr(suite, suite->pana_session.auth_info); buffer_free(buf); return; } avp_temp.code = AVP_SESSION_LIFETIME_CODE; if (pana_avp_discover(ptr, length, &avp_temp) && avp_temp.len == 4) { lifetime = common_read_32_bit(avp_temp.avp_ptr); } if (!key_id_parsed) { goto pana_failure; } suite->pana_session.pana_key_id = key_id; suite->pana_session.session_lifetime = lifetime; if (suite->state != PANA_READY) { pana_key_calculation(suite); } if (!pana_message_authency_validate(ptr, length, suite->pana_session.pana_auth_key)) { goto pana_failure; } avp_temp.code = AVP_ENCRYPT_ALGORITHM_CODE; if (pana_avp_discover(ptr, length, &avp_temp)) { tr_debug("Key Delivery"); if (pana_ccm_data_crypt(avp_temp.avp_ptr, avp_temp.len, AES_CCM_DECRYPT, header->seq, suite) != 0) { goto pana_failure; } uint8_t *key_material = pana_client_keywrap_parse(avp_temp.avp_ptr, avp_temp.len, suite); if (!key_material) { goto pana_failure; } if (!suite->pana_session.auth_info) { goto pana_failure; } tr_debug("Valid"); suite->pana_session.nwk_key_id = key_material[16]; suite->pana_session.auth_cnt = key_material[17]; auth_info_t *pana_auth_info_temp = suite->pana_session.auth_info; memcpy(pana_auth_info_temp->network_key, key_material, 16); pana_auth_info_temp->key_id = key_material[16]; } tr_debug("Server AUTH_OK"); suite->state = PANA_READY; suite->timer = 95; build_response: buf = pana_auth_msg_build(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, false, suite); protocol_push(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_client_pna_handler(buffer_t *buf, pana_header_t *header, sec_suite_t *suite) { protocol_interface_info_entry_t *cur = buf->interface; if (!cur) { goto end_of_function; } if (!suite->pana_session.session_ready) { goto end_of_function; } uint16_t length = buffer_data_length(buf); uint8_t *ptr = buffer_data_pointer(buf); tr_debug("Parse Ping Auth Notify"); if (!pana_message_authency_validate(ptr, length, suite->pana_session.pana_auth_key)) { tr_warn("Auth Fail"); goto end_of_function; } pana_avp_t avp_temp; avp_temp.code = AVP_ENCRYPT_ALGORITHM_CODE; if (pana_avp_discover(ptr, length, &avp_temp)) { tr_debug("ZIP Key"); //Calc key if (pana_ccm_data_crypt(avp_temp.avp_ptr, avp_temp.len, AES_CCM_DECRYPT, header->seq, suite) != 0) { tr_debug("Drop Key MSG"); goto end_of_function; } uint8_t *key_delivery = pana_client_keywrap_parse(avp_temp.avp_ptr, avp_temp.len, suite); if (!key_delivery) { tr_debug("Drop Key MSG"); goto end_of_function; } //tr_debug("Valid"); suite->pana_session.nwk_key_id = key_delivery[16]; suite->pana_session.auth_cnt = key_delivery[17]; if (suite->state == PANA_PING_REQ) { tr_debug("Save Key to auth Info"); if (suite->pana_session.auth_info) { tr_debug("Save Key to auth Info"); auth_info_t *pana_auth_info_temp = suite->pana_session.auth_info; //Save keys to Auth Info structure memcpy(pana_auth_info_temp->network_key, key_delivery, 16); pana_auth_info_temp->key_id = key_delivery[16]; } } else { //Calc keys tr_debug("Calc keys"); uint8_t *key_ptr = pana_key_get(key_delivery); mac_helper_security_next_key_set(cur, (key_ptr + 16), key_delivery[16], MAC_KEY_ID_MODE_IDX); mle_service_security_set_security_key(cur->id, key_ptr, key_delivery[16], false); } } if (header->flags & PANA_FLAGS_REQUEST) { //tr_debug("Build Response"); //SET Original packet First if ((header->flags & (PANA_FLAGS_PING | PANA_FLAGS_REQUEST)) == (PANA_FLAGS_PING | PANA_FLAGS_REQUEST)) { pana_client_session_nvm_udate(suite); } header->flags &= ~PANA_FLAGS_REQUEST; buf->buf_ptr = PANA_HEADER_LENGTH; ptr = buffer_data_pointer(buf); 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) { pana_auth_hash_calc(buffer_data_pointer(buf), buffer_data_length(buf), suite->pana_session.pana_auth_key); //memcpy(buf->dst_sa.address, buf->src_sa.address, 16); //buf->src_sa.addr_type = ADDR_NONE; if ((cur->lowpan_info & INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY)) { pana_set_agend_address(buf, true, suite); } else { pana_set_agend_address(buf, false, suite); } protocol_push(buf); } return; } //tr_debug("GET KEY"); if (suite->state == PANA_PING_REQ) { //tr_debug("RE Valid Ready"); tr_debug("pana nvm ok"); sec_lib_state_machine_trig(suite, PANA_RE_VALID); } else if (suite->state == PANA_KEY_PULL) { mac_data_poll_protocol_poll_mode_disable(cur); sec_lib_state_machine_trig(suite, PANA_PULL_DONE); } pana_client_session_nvm_udate(suite); end_of_function: buffer_free(buf); } 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_client_session_nvm_udate(suite); } //Kick ICMP-State Machine to ND / RPL if (suite->state != PANA_PULL_DONE) { pana_authentication_ready(1, suite->interface); } //Reset pointer suite->pana_session.auth_info = NULL; pana_free_dynamic_ram(suite); sec_suite_tls_free(suite, true); } static void pana_pci_build(sec_suite_t *suite) { buffer_t *buf = buffer_get(127); if (!buf) { tr_debug("Pana Init Fail"); return; } tr_debug("BUILD PCI"); pana_header_t header; header.type = PANA_MSG_PCI; header.flags = 0; header.seq = 0; header.session_id = 0; buf->interface = suite->interface; pana_set_agend_address(buf, false, suite); suite->session_port = UDP_PORT_PANA; buf = build_pana_base(buf, &header, suite); protocol_push(buf); } static void pana_client_pana_error_handler(sec_suite_t *suite) { sec_lib_state_machine_lock(suite, PANA_ERROR); pana_authentication_ready(0, suite->interface); seclib_session_clean(suite); } static void pana_client_state_machine_func(sec_suite_t *suite) { if (!suite) { return; } uint8_t general_tx = 0; switch (suite->state) { case PANA_ERROR: pana_client_pana_error_handler(suite); return; case PANA_PCI_TX: if (sec_auth_re_check(suite)) { pana_pci_build(suite); pana_timeout_timer_set(suite, suite->state); } else { pana_client_pana_error_handler(suite); } return; case PANA_KEY_PULL: case PANA_PING_REQ: if (sec_auth_re_check(suite)) { if (suite->state == PANA_PING_REQ) { pana_ping_notify_msg_generate(0, suite); } else { pana_ping_notify_msg_generate(1, suite); } } else { //Do Pana if (suite->state == PANA_PING_REQ) { tr_error("pana nvm failed"); } tr_debug("Reset Pana"); suite->pana_session.session_ready = false; pana_session_init_by_session_ptr(suite, suite->pana_session.auth_info); } break; 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_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_CLIENT_TX_CERTIFICATE_VERIFY: #ifdef ECC if (sec_auth_re_check(suite)) { if (tls_certificate_build(suite) == 0) { tr_warn("Client Certi Verify Alloc fail"); suite->timer = 4; } else { sec_set_auth_timeout(suite, TLS_CLIENT_TX_CERTIFICATE_VERIFY); } } else #endif { 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_START_RESPONSE: pana_start_message_build(buf, suite); break; case EAP_IDENTITY_RES: pana_eap_identity_build(buf, suite); break; case TLS_INIT: buf = tls_client_hello_build(buf, suite); if (buf) { pana_eap_down(buf, suite); } sec_set_auth_timeout(suite, TLS_INIT); break; case TLS_KEY_CHANGE: //Print Handshake message tls_prepare_change_chipher_spec(suite); tls_build_client_change_chipher_suite_finnish(buf, suite); pana_eap_down(buf, suite); break; case PANA_FAILURE: buf->buf_ptr = PANA_HEADER_LENGTH; buf->buf_end = PANA_HEADER_LENGTH; 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_KEY_CHANGE: sec_lib_state_machine_trig(suite, TLS_ALERT_INTERNAL); break; case TLS_INIT: //Trig pana failure if not get any response from server sec_lib_state_machine_trig(suite, PANA_FAILURE); break; default: pana_client_pana_error_handler(suite); break; } } } } static void pana_client_packet_handler(buffer_t *buf) { 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); sec_suite_t *suite = sec_suite_selected_py_pan_id(buf->link_specific.ieee802_15_4.srcPanId); if (!suite) { buffer_free(buf); return; } header.agent_retry = false; //Handle Relay if (header.type == PANA_MSG_RELAY) { if (suite->pana_session.session_ready) { buf = pana_relay_parse(buf); } if (buf) { protocol_interface_info_entry_t *cur_interface = buf->interface; addr_interface_get_ll_address(cur_interface, buf->src_sa.address, 0); buf->src_sa.addr_type = ADDR_IPV6; buf->src_sa.port = UDP_PORT_PANA; buf = buffer_turnaround(buf); buf->info = (buffer_info_t)(B_DIR_DOWN + B_FROM_APP + B_TO_UDP); protocol_push(buf); buf = NULL; } return; } bool my_session = false; if (header.type != PANA_MSG_PCI) { if ((header.flags & PANA_FLAG_START_REQ_MASK) == (PANA_FLAG_START_REQ_MASK)) { if (memcmp(suite->session_address, buf->src_sa.address, 16) == 0) { my_session = true; } } else { //Check Session if (suite->pana_session.session_id == header.session_id) { //Validate Session if ((memcmp(suite->session_address, buf->src_sa.address, 16) == 0)) { my_session = true; } else if (memcmp(suite->pana_session.session_relay_address, buf->src_sa.address, 16) == 0) { my_session = true; } } if (!my_session) { if (buf->src_sa.address[0] != 0xfe) { tr_debug("Not relay src"); buffer_free(buf); return; } } } } if (my_session) { if ((header.flags & PANA_FLAG_START_REQ_MASK) == (PANA_FLAG_START_REQ_MASK)) { tr_debug("Take session & Sequency"); suite->pana_session.session_id = header.session_id; suite->pana_session.res_seq = header.seq; pana_session_startms_parse(buf, &header, suite); return; } if (suite->pana_session.pana_heap) { pana_heap_t *pheap = suite->pana_session.pana_heap; if (pheap->handshake_len == 0 || pheap->handshake_req_offset == pheap->handshake_len) { tr_debug("Pana REQ dropped because Handshake is not full started"); 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++; } else { tr_debug("Pana RES:Drop Packet by seq num. Res: %02"PRIu32", RX: %02"PRIu32, suite->pana_session.req_seq >> 8, header.seq); buffer_free(buf); return; } } 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("PANA REQ:Drop Packet by seq num. Res: %02"PRIu32" , RX: %02"PRIu32, suite->pana_session.res_seq >> 8, header.seq); buffer_free(buf); return; } else { header.agent_retry = true; } } } if (header.flags & PANA_FLAGS_COMPLETE) { pana_complete_msg_parse(buf, &header, suite); } else { if (header.type == PANA_MSG_PNA) { pana_client_pna_handler(buf, &header, suite); return; } if (header.type != PANA_MSG_PA) { return; } buf = pana_auth_message_handler(buf, &header, suite); if (buf) { pana_eap_tls_up(buf, suite); } } return; } //Relay if ((suite->pana_session.session_ready) && pana_check_address(buf)) { if (header.type == PANA_MSG_PNA || header.type == PANA_MSG_PCI) { //Remove old data if (lowpan_neighbour_data_clean(suite->interface->id, buf->src_sa.address)) { uint8_t ll_adr[16]; memcpy(ll_adr, buf->src_sa.address, 16); buffer_free(buf); tr_debug("Parent rejoin --> send unsecured Link Reject"); mle_service_reject_message_build(suite->interface->id, ll_adr, true); return; } } buffer_data_reserve_header(buf, 16); buf = pana_relay_avp_build(buf, suite); if (buf) { //Set Pana Headers Like Draft say tr_debug("Pana Relay"); header.flags = 0; header.type = PANA_MSG_RELAY; header.session_id = 0; header.seq = 0; pana_set_agend_address(buf, true, suite); buf = build_pana_base(buf, &header, suite); protocol_push(buf); buf = NULL; } else { tr_debug("Relay AVP Build Fail"); } } if (buf) { buffer_free(buf); } return; } sec_suite_t *pana_client_init(auth_info_t *auth_ptr, uint8_t *session_address_ptr, pana_tls_setup_s *setup) { sec_suite_t *suite = sec_suite_selected_py_pan_id(setup->pan_id); if (!suite) { bool loaded_setup = false; if (pana_client_nvm_storage_cb) { loaded_setup = pana_client_session_get(setup->pan_id); if (loaded_setup) { tr_debug("load pana nvm for PAN ID %2.2x", setup->pan_id); suite = sec_lib_security_session_allocate(false); } else { tr_debug("nvm pana load fail for PAN ID %2.2x", setup->pan_id); suite = sec_lib_security_session_allocate(true); } } else { suite = sec_lib_security_session_allocate(true); } if (!suite) { return NULL; } if (loaded_setup) { pana_session_data_load_to_session(pana_client_nvm_buffer + 16, suite); } suite->pan_id = setup->pan_id; tr_debug("Create Entry"); memcpy(suite->session_address, session_address_ptr, 16); tr_debug("Session adr: %s", tr_ipv6(suite->session_address)); } if (suite) { pana_session_init_by_session_ptr(suite, auth_ptr); suite->supported_chipher_suites = setup->security_support; suite->psk_key_id = setup->psk_key_id; } return suite; } void pana_reset_client_session(void) { sec_suite_list_clean(); } int8_t pana_client_interface_init(int8_t interface_id, net_tls_cipher_e cipher_mode, uint32_t psk_key_id) { protocol_interface_info_entry_t *cur = 0; cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur) { return -1; } if (!cur->if_lowpan_security_params) { return -1; } else 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; } if (!pana_socket_init(pana_client_packet_handler, pana_client_state_machine_func, tls_client_up)) { return -1; } switch (cipher_mode) { case NET_TLS_PSK_CIPHER: /**< Network Authentication support only PSK */ //Verify PSK KEY ID if (arm_tls_check_key(psk_key_id) != 0) { return -7; } break; case NET_TLS_PSK_AND_ECC_CIPHER: /**< Network Authentication support PSK & ECC */ //Verify PSK KEY ID if (arm_tls_check_key(psk_key_id) != 0) { return -7; } /* fall through */ case NET_TLS_ECC_CIPHER: /**< Network Authentication support only ECC */ #ifdef ECC //Verify Certficate if (sec_cetificate_chain_get(SEC_NWK_AUTHENTICATION_CERTI_CHAIN) == NULL) { return -6; } #endif break; } cur->if_lowpan_security_params->pana_params->nwk_chipher_mode = cipher_mode; cur->if_lowpan_security_params->pana_params->psk_key_id = psk_key_id; cur->if_lowpan_security_params->pana_params->pana_client = 1; cur->lowpan_info |= (INTERFACE_NWK_BOOTSRAP_PANA_AUTHENTICATION); cur->configure_flags |= INTERFACE_SECURITY_DEFINED; return 0; } nwk_pana_params_s *pana_client_parameter_allocate(void) { nwk_pana_params_s *pana_params = ns_dyn_mem_alloc((sizeof(nwk_pana_params_s))); if (pana_params) { #ifdef ECC pana_params->nwk_chipher_mode = NET_TLS_ECC_CIPHER; #else pana_params->nwk_chipher_mode = NET_TLS_PSK_CIPHER; #endif pana_params->client_session_mode = NET_PANA_SINGLE_SESSION; pana_params->psk_key_id = 0; pana_params->pana_client = 1; } return pana_params; } int8_t pana_client_key_pull(int8_t interface_id) { protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id); if (!cur || !(cur->lowpan_info & INTERFACE_NWK_ACTIVE)) { return -1; } sec_suite_t *suite = sec_suite_selected_py_pan_id(cur->mac_parameters->pan_id); if (!suite || suite->pana_session.user_server || !suite->pana_session.session_ready || suite->state == PANA_KEY_PULL) { return -1; } mac_data_poll_enable_check(cur); sec_lib_state_machine_trig(suite, PANA_KEY_PULL); return 0; } uint8_t pana_ping_notify_msg_tx(uint16_t pan_id) { sec_suite_t *suite = sec_suite_selected_py_pan_id(pan_id); if (!suite) { return 0; } sec_lib_state_machine_trig(suite, PANA_KEY_PULL); return 1; } static uint8_t pana_ping_notify_msg_generate(uint8_t key_req, sec_suite_t *suite) { if (suite->pana_session.user_server) { return 0; } protocol_interface_info_entry_t *cur = suite->interface; if (!cur) { return 0; } buffer_t *buf = buffer_get(127); if (!buf) { return 0; } uint8_t *ptr; pana_header_t header; header.flags = PANA_FLAGS_REQUEST | PANA_FLAGS_PING; header.type = PANA_MSG_PNA; 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); if (key_req) { //Add Key Request tr_debug("Key Request"); ptr = pana_avp_zip_key_req(ptr, cur); } 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 0; } //Calc //tr_debug("Calc"); pana_auth_hash_calc(buffer_data_pointer(buf), buffer_data_length(buf), suite->pana_session.pana_auth_key); if (cur->lowpan_info & INTERFACE_NWK_ROUTER_DEVICE) { if (suite->state == PANA_PING_REQ) { pana_set_agend_address(buf, false, suite); } else { if ((cur->lowpan_info & INTERFACE_NWK_BOOTSRAP_ADDRESS_REGISTER_READY)) { pana_set_agend_address(buf, true, suite); } else { pana_set_agend_address(buf, false, suite); } } } else { pana_set_agend_address(buf, false, suite); } // tr_debug("Hash Cal: %s", trace_array(ptr,16)); if (key_req) { pana_timeout_timer_set(suite, PANA_KEY_PULL); tr_debug("Pull Key by Req"); } else { tr_debug("TX Ping notify"); pana_timeout_timer_set(suite, PANA_PING_REQ); } protocol_push(buf); return 1; } int8_t pana_client_nvm_callback_set(pana_client_session_update_cb *nvm_update, pana_client_session_get_cb *nvm_get, uint8_t *nvm_static_buffer) { if (!nvm_update || !nvm_static_buffer || !nvm_get) { return -1; } pana_client_nvm_storage_cb = nvm_update; pana_client_session_get = nvm_get; pana_client_nvm_buffer = nvm_static_buffer; return 0; } static void pana_client_session_nvm_udate(sec_suite_t *suite) { if (pana_client_nvm_storage_cb) { uint8_t *data_buf = pana_client_nvm_buffer; memcpy(data_buf, suite->session_address, 16); data_buf += 16; *data_buf++ = suite->pana_session.auth_cnt; *data_buf++ = suite->pana_session.nwk_key_id; memcpy(data_buf, suite->pana_session.pana_auth_key, 32); data_buf += 32; memcpy(data_buf, suite->pana_session.pana_PAA_enc_key, 16); data_buf += 16; data_buf = common_write_32_bit(suite->pana_session.pana_key_id, data_buf); data_buf = common_write_32_bit(suite->pana_session.session_id, data_buf); data_buf = common_write_32_bit(suite->pana_session.req_seq, data_buf); data_buf = common_write_32_bit(suite->pana_session.res_seq, data_buf); data_buf = common_write_32_bit(suite->pana_session.session_lifetime, data_buf); pana_client_nvm_storage_cb(suite->pan_id, PANA_CLIENT_SESSION_UPDATE); } } static void pana_session_data_load_to_session(uint8_t *data_buf, sec_suite_t *suite) { suite->timer = 0; suite->supported_chipher_suites = SEC_DEFAULT_SUPPORTED_CHIPHER_SUITES; suite->setups = 0; pana_session_state_init(&suite->pana_session); //Check Is pana Raedy suite->pana_session.session_ready = true; //Start Copy //tr_debug("Set Status"); suite->pana_session.auth_cnt = *data_buf++; suite->pana_session.nwk_key_id = *data_buf++; memcpy(suite->pana_session.pana_auth_key, data_buf, 32); data_buf += 32; memcpy(suite->pana_session.pana_PAA_enc_key, data_buf, 16); data_buf += 16; suite->pana_session.pana_key_id = common_read_32_bit(data_buf); data_buf += 4; suite->pana_session.session_id = common_read_32_bit(data_buf); data_buf += 4; suite->pana_session.req_seq = common_read_32_bit(data_buf); data_buf += 4; suite->pana_session.res_seq = common_read_32_bit(data_buf); data_buf += 4; suite->pana_session.session_lifetime = common_read_32_bit(data_buf); } #else int8_t pana_client_nvm_callback_set(pana_client_session_update_cb *nvm_update, pana_client_session_get_cb *nvm_get, uint8_t *nvm_static_buffer) { (void)nvm_update; (void)nvm_get; (void)nvm_static_buffer; return -1; } #endif