/* * * Copyright (c) 2022 Project CHIP Authors * * 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. */ #ifdef SL_WFX_USE_SECURE_LINK /* Includes */ #include "sl_wfx.h" #include #include "mbedtls/ccm.h" #include "mbedtls/ctr_drbg.h" #include "mbedtls/ecdh.h" #include "mbedtls/entropy.h" #include "mbedtls/md.h" #include "mbedtls/sha256.h" #include "FreeRTOS.h" #include "queue.h" #include "semphr.h" #include "task.h" // Secure link MAC key location for WGM160P (in DI page in flash) #ifdef EFM32GG11B820F2048GM64 // WGM160PX22KGA2 #define SL_WFX_FCCC_BASE_ADDR ((void *) 0x0fe08000ul) #define SL_WFX_FCCC_DI_OFFSET 0x1B0ul #define SL_WFX_FCCC_DI_ADDR ((void *) (SL_WFX_FCCC_BASE_ADDR + SL_WFX_FCCC_DI_OFFSET)) #define SL_WFX_SECURE_LINK_MAC_KEY_LOCATION ((void *) (SL_WFX_FCCC_BASE_ADDR + 0x3D0)) #endif /****************************************************** * Macros ******************************************************/ #define MAC_KEY_FAIL_BYTE 0XFF #define KEY_DIGEST_SIZE 92 #define MEMCMP_FAIL 0 #define MPI_SET 1 #define SUCCESS_STATUS_WIFI_SECURE_LINK_EXCHANGE 0 #define SHA224_0 0 #define HMAC_SIZE 92 #define MEMSET_LEN 1 #define LABLE_LEN 24 #define ADDRESS_LENGTH 0 #define CCM_STATUS_SUCCESS 0 /****************************************************** * Constants ******************************************************/ /* Semaphore to signal wfx driver available */ extern TaskHandle_t wfx_securelink_task; extern SemaphoreHandle_t wfx_securelink_rx_mutex; /****************************************************** * Enumerations ******************************************************/ /****************************************************** * Type Definitions ******************************************************/ /****************************************************** * Structures ******************************************************/ /****************************************************** * Function Declarations ******************************************************/ static inline void reverse_bytes(uint8_t * src, uint8_t length); /****************************************************** * Variable Definitions ******************************************************/ #if SL_WFX_SLK_CURVE25519 static mbedtls_ecdh_context mbedtls_host_context; static mbedtls_ctr_drbg_context host_drbg_context; #endif static mbedtls_entropy_context entropy; uint8_t temp_key_location[SL_WFX_HOST_PUB_KEY_MAC_SIZE]; #ifdef EFM32GG11B820F2048GM64 // WGM160PX22KGA2 static const uint8_t * const secure_link_mac_key = (uint8_t *) SL_WFX_SECURE_LINK_MAC_KEY_LOCATION; #else static const uint8_t secure_link_mac_key[SL_WFX_SECURE_LINK_MAC_KEY_LENGTH] = { 0x2B, 0x49, 0xFD, 0x66, 0xCB, 0x74, 0x6D, 0x6B, 0x4F, 0xDC, 0xC3, 0x79, 0x4E, 0xC5, 0x9A, 0x86, 0xE5, 0x48, 0x2A, 0x41, 0x22, 0x87, 0x8B, 0x12, 0x1A, 0x7C, 0x3E, 0xEF, 0xB7, 0x04, 0x9E, 0xB3 }; #endif /****************************************************** * Function Definitions ******************************************************/ /**************************************************************************** * @fn sl_status_t sl_wfx_host_get_secure_link_mac_key(uint8_t *sl_mac_key) * @brief * Get secure link mac key * @param[in] sl_mac_key: * @return returns SL_STATUS_OK *****************************************************************************/ sl_status_t sl_wfx_host_get_secure_link_mac_key(uint8_t * sl_mac_key) { sl_status_t result = SL_STATUS_WIFI_SECURE_LINK_MAC_KEY_ERROR; memcpy(sl_mac_key, secure_link_mac_key, SL_WFX_SECURE_LINK_MAC_KEY_LENGTH); for (uint8_t index = 0; index < SL_WFX_SECURE_LINK_MAC_KEY_LENGTH; ++index) { // Assuming 0xFF... when not written if (sl_mac_key[index] != MAC_KEY_FAIL_BYTE) { result = SL_STATUS_OK; break; } } return result; } /**************************************************************************** * @fn sl_status_t sl_wfx_host_compute_pub_key(sl_wfx_securelink_exchange_pub_keys_req_body_t *request, const uint8_t *sl_mac_key) * @brief * compute host public key * @param[in] request : * @param[in] sl_mac_key : * @return returns SL_STATUS_OK *****************************************************************************/ sl_status_t sl_wfx_host_compute_pub_key(sl_wfx_securelink_exchange_pub_keys_req_body_t * request, const uint8_t * sl_mac_key) { sl_status_t status = SL_STATUS_OK; #if SL_WFX_SLK_CURVE25519 const char identifier[] = "ecdh"; mbedtls_ecdh_init(&mbedtls_host_context); mbedtls_ctr_drbg_init(&host_drbg_context); mbedtls_entropy_init(&entropy); status = mbedtls_ctr_drbg_seed(&host_drbg_context, mbedtls_entropy_func, &entropy, (const unsigned char *) identifier, sizeof(identifier)); status += mbedtls_ecp_group_load(&mbedtls_host_context.grp, MBEDTLS_ECP_DP_CURVE25519); status += mbedtls_ecdh_gen_public(&mbedtls_host_context.grp, &mbedtls_host_context.d, &mbedtls_host_context.Q, mbedtls_ctr_drbg_random, &host_drbg_context); status += mbedtls_mpi_write_binary(&mbedtls_host_context.Q.X, request->host_pub_key, SL_WFX_HOST_PUB_KEY_SIZE); #else mbedtls_entropy_init(&entropy); status = mbedtls_entropy_func(&entropy, request->host_pub_key, SL_WFX_HOST_PUB_KEY_SIZE); #endif reverse_bytes(request->host_pub_key, SL_WFX_HOST_PUB_KEY_SIZE); SL_WFX_ERROR_CHECK(status); // Generate SHA512 digest of public key status = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), sl_mac_key, SL_WFX_HOST_PUB_KEY_SIZE, request->host_pub_key, SL_WFX_HOST_PUB_KEY_SIZE, request->host_pub_key_mac); SL_WFX_ERROR_CHECK(status); error_handler: if (status != SL_STATUS_OK) { return SL_STATUS_WIFI_SECURE_LINK_EXCHANGE_FAILED; } return status; } /**************************************************************************** * @fn sl_status_t sl_wfx_host_verify_pub_key(sl_wfx_securelink_exchange_pub_keys_ind_t *response_packet, const uint8_t *sl_mac_key, uint8_t *sl_host_pub_key) * @brief * verify host public key * @param[in] response_packet: * @param[in] sl_mac_key: * @param[in] sl_host_pub_key: * @return returns SL_STATUS_OK if successful, * SL_STATUS_FAIL otherwise *****************************************************************************/ sl_status_t sl_wfx_host_verify_pub_key(sl_wfx_securelink_exchange_pub_keys_ind_t * response_packet, const uint8_t * sl_mac_key, uint8_t * sl_host_pub_key) { sl_status_t status = SL_STATUS_OK; uint8_t shared_key_digest[KEY_DIGEST_SIZE]; if (xSemaphoreTake(wfx_securelink_rx_mutex, portMAX_DELAY) != pdTRUE) { return SL_STATUS_WIFI_SECURE_LINK_EXCHANGE_FAILED; } // Compute the Hash and verify the public key/hashing status = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), sl_mac_key, SL_WFX_NCP_PUB_KEY_SIZE, response_packet->body.ncp_pub_key, SL_WFX_NCP_PUB_KEY_SIZE, temp_key_location); SL_WFX_ERROR_CHECK(status); // Calculate session key if public key/SHA512 digest matches if (memcmp(temp_key_location, response_packet->body.ncp_pub_key_mac, SL_WFX_HOST_PUB_KEY_MAC_SIZE) != MEMCMP_FAIL) { status = SL_STATUS_WIFI_SECURE_LINK_EXCHANGE_FAILED; goto error_handler; } #if SL_WFX_SLK_CURVE25519 SL_WFX_UNUSED_PARAMETER(sl_host_pub_key); mbedtls_mpi_lset(&mbedtls_host_context.Qp.Z, MPI_SET); // Read Ineo public key reverse_bytes(response_packet->body.ncp_pub_key, SL_WFX_NCP_PUB_KEY_SIZE); mbedtls_mpi_read_binary(&mbedtls_host_context.Qp.X, response_packet->body.ncp_pub_key, SL_WFX_NCP_PUB_KEY_SIZE); // Calculate shared secret if (mbedtls_ecdh_compute_shared(&mbedtls_host_context.grp, &mbedtls_host_context.z, &mbedtls_host_context.Qp, &mbedtls_host_context.d, mbedtls_ctr_drbg_random, &host_drbg_context) != SUCCESS_STATUS_WIFI_SECURE_LINK_EXCHANGE) { status = SL_STATUS_WIFI_SECURE_LINK_EXCHANGE_FAILED; goto error_handler; } // Generate session key mbedtls_mpi_write_binary(&mbedtls_host_context.z, temp_key_location, SL_WFX_HOST_PUB_KEY_SIZE); reverse_bytes(temp_key_location, SL_WFX_HOST_PUB_KEY_SIZE); mbedtls_sha256(temp_key_location, SL_WFX_HOST_PUB_KEY_SIZE, shared_key_digest, SHA224_0); #else uint8_t hmac_input[HMAC_SIZE] = { 0 }; char label[LABLE_LEN] = "SecureLink!KeyDerivation"; memset((uint16_t *) &hmac_input[0], (uint16_t) sl_wfx_htole16(1), MEMSET_LEN); memcpy((uint8_t *) &hmac_input[2], (uint8_t *) label, LABLE_LEN); memcpy((uint8_t *) &hmac_input[26], sl_host_pub_key, SL_WFX_NCP_PUB_KEY_SIZE); memcpy((uint8_t *) &hmac_input[58], (uint8_t *) response_packet->body.ncp_pub_key, SL_WFX_NCP_PUB_KEY_SIZE); memset((uint16_t *) &hmac_input[90], (uint16_t) sl_wfx_htole16(128), 1); // Generate SHA256 digest of hmac_input status = mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), sl_mac_key, SL_WFX_HOST_PUB_KEY_SIZE, (uint8_t *) hmac_input, HMAC_SIZE, shared_key_digest); #endif memcpy(sl_wfx_context->secure_link_session_key, shared_key_digest, SL_WFX_SECURE_LINK_SESSION_KEY_LENGTH); // Use the lower 16 bytes of the sha256 sl_wfx_context->secure_link_nonce.hp_packet_count = 0; sl_wfx_context->secure_link_nonce.rx_packet_count = 0; sl_wfx_context->secure_link_nonce.tx_packet_count = 0; error_handler: if (xSemaphoreGive(wfx_securelink_rx_mutex) != pdTRUE) { printf("ERROR: sl_wfx_securelink_rx_mutex. unable to post.\n"); } return status; } /**************************************************************************** * @fn sl_status_t sl_wfx_host_free_crypto_context(void) * @brief * Free host crypto context * @param[in] None * @return returns SL_STATUS_OK *****************************************************************************/ sl_status_t sl_wfx_host_free_crypto_context(void) { #if SL_WFX_SLK_CURVE25519 mbedtls_ecdh_free(&mbedtls_host_context); mbedtls_ctr_drbg_free(&host_drbg_context); #endif mbedtls_entropy_free(&entropy); return SL_STATUS_OK; } /******************************************************************************** * @fn sl_status_t sl_wfx_host_decode_secure_link_data(uint8_t *buffer, uint32_t length, uint8_t *session_key) * @brief * Decode receive data * Length excludes size of CCM tag and secure link header * @param[in] buffer: * @param[in] length: * @param[in] session_key: * @return returns SL_STATUS_OK if successful, * SL_STATUS_FAIL otherwise ********************************************************************************/ sl_status_t sl_wfx_host_decode_secure_link_data(uint8_t * buffer, uint32_t length, uint8_t * session_key) { mbedtls_ccm_context ccm_context; sl_status_t status = SL_STATUS_SECURITY_DECRYPT_ERROR; int crypto_status; sl_wfx_nonce_t nonce = { 0, 0, 0 }; if (xSemaphoreTake(wfx_securelink_rx_mutex, portMAX_DELAY) != pdTRUE) { return SL_STATUS_FAIL; } // Nonce for decryption should have TX and HP counters 0, only use RX counter nonce.rx_packet_count = sl_wfx_context->secure_link_nonce.rx_packet_count; // Init context mbedtls_ccm_init(&ccm_context); // Set the crypto key crypto_status = mbedtls_ccm_setkey(&ccm_context, MBEDTLS_CIPHER_ID_AES, session_key, SL_WFX_SECURE_LINK_SESSION_KEY_BIT_COUNT); SL_WFX_ERROR_CHECK(crypto_status); // Decrypt the data if (!mbedtls_ccm_auth_decrypt(&ccm_context, length, (uint8_t *) &nonce, SL_WFX_SECURE_LINK_NONCE_SIZE_BYTES, NULL, ADDRESS_LENGTH, (uint8_t *) buffer, (uint8_t *) buffer, (uint8_t *) buffer + length, SL_WFX_SECURE_LINK_CCM_TAG_SIZE)) { status = SL_STATUS_OK; } error_handler: mbedtls_ccm_free(&ccm_context); if (xSemaphoreGive(wfx_securelink_rx_mutex) != pdTRUE) { printf("ERROR: sl_wfx_securelink_rx_mutex. unable to post.\n"); } return status; } /********************************************************************* * @fn sl_status_t sl_wfx_host_encode_secure_link_data(sl_wfx_generic_message_t *buffer, uint32_t data_length, uint8_t *session_key, uint8_t *nonce) * @brief * Encode transmit data * Length excludes size of CCM tag and secure link header * @param[in] buffer: * @param[in] data_length: * @param[in] session_key: * @param[in] nonce: * @return returns SL_STATUS_OK if successful, * SL_STATUS_FAIL otherwise *************************************************************************/ sl_status_t sl_wfx_host_encode_secure_link_data(sl_wfx_generic_message_t * buffer, uint32_t data_length, uint8_t * session_key, uint8_t * nonce) { mbedtls_ccm_context ccm_context; sl_status_t status = SL_STATUS_FAIL; mbedtls_ccm_init(&ccm_context); if (mbedtls_ccm_setkey(&ccm_context, MBEDTLS_CIPHER_ID_AES, session_key, SL_WFX_SECURE_LINK_SESSION_KEY_BIT_COUNT) == CCM_STATUS_SUCCESS) { mbedtls_ccm_encrypt_and_tag(&ccm_context, data_length, nonce, SL_WFX_SECURE_LINK_NONCE_SIZE_BYTES, NULL, ADDRESS_LENGTH, (uint8_t *) &buffer->header.id, (uint8_t *) &buffer->header.id, (uint8_t *) &buffer->header.id + data_length, SL_WFX_SECURE_LINK_CCM_TAG_SIZE); status = SL_STATUS_OK; } mbedtls_ccm_free(&ccm_context); return status; } /**************************************************************************** * @fn sl_status_t sl_wfx_host_schedule_secure_link_renegotiation(void) * @brief * Called when the driver needs to schedule secure link renegotiation * @param[in] None * @returns Returns SL_STATUS_OK if successful, * SL_STATUS_FAIL otherwise *****************************************************************************/ sl_status_t sl_wfx_host_schedule_secure_link_renegotiation(void) { // call sl_wfx_secure_link_renegotiate_session_key() as soon as it makes sense for the host to do so xTaskNotifyGive(wfx_securelink_task); return SL_STATUS_OK; } /**************************************************************************** * @fn static inline void reverse_bytes(uint8_t *src, uint8_t length) * @brief * reverse the bytes * @param[in] src: source * @param[in] length: * @returns None *****************************************************************************/ static inline void reverse_bytes(uint8_t * src, uint8_t length) { uint8_t * lo = src; uint8_t * hi = src + length - 1; uint8_t swap; while (lo < hi) { swap = *lo; *lo++ = *hi; *hi-- = swap; } } /******************************************************************************************************** ******************************************************************************************************** * DEPENDENCIES & AVAIL CHECK(S) ******************************************************************************************************** *******************************************************************************************************/ #endif // SL_WFX_USE_SECURE_LINK