/*! * \file soft-se.c * * \brief Secure Element software implementation * * \copyright Revised BSD License, see section \ref LICENSE. * * \code * ______ _ * / _____) _ | | * ( (____ _____ ____ _| |_ _____ ____| |__ * \____ \| ___ | (_ _) ___ |/ ___) _ \ * _____) ) ____| | | || |_| ____( (___| | | | * (______/|_____)_|_|_| \__)_____)\____)_| |_| * (C)2020 Semtech * * ___ _____ _ ___ _ _____ ___ ___ ___ ___ * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| * embedded.connectivity.solutions=============== * * \endcode * * \author Miguel Luis ( Semtech ) * * \author Gregory Cristian ( Semtech ) * * \author Daniel Jaeckle ( STACKFORCE ) * * \author Johannes Bruder ( STACKFORCE ) */ /** ****************************************************************************** * * Portions COPYRIGHT 2020 STMicroelectronics * * @file soft-se.c * @author MCD Application Team * @brief Secure Element software implementation ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include #include #include "lorawan_conf.h" /* LORAWAN_KMS */ #include "radio.h" /* needed for Random */ #include "utilities.h" #include "LoRaMacHeaderTypes.h" #include "secure-element.h" #include "se-identity.h" #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) #include "lorawan_aes.h" #include "cmac.h" #else /* LORAWAN_KMS == 1 */ #include "mw_log_conf.h" /* needed for MW_LOG */ #include "kms_if.h" #endif /* LORAWAN_KMS */ /* Private constants ---------------------------------------------------------*/ /*! * Number of supported crypto keys */ #if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) #if ( LORAMAC_MAX_MC_CTX == 1 ) #define NUM_OF_KEYS 14UL #else /* LORAMAC_MAX_MC_CTX > 1 */ #define NUM_OF_KEYS 23UL #endif /* LORAMAC_MAX_MC_CTX */ #else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ #if ( LORAMAC_MAX_MC_CTX == 1 ) #define NUM_OF_KEYS 10UL #else /* LORAMAC_MAX_MC_CTX > 1 */ #define NUM_OF_KEYS 19UL #endif /* LORAMAC_MAX_MC_CTX */ #endif /* USE_LRWAN_1_1_X_CRYPTO */ /*! * MIC computation offset * \remark required for 1.1.x support */ #define CRYPTO_MIC_COMPUTATION_OFFSET ( JOIN_REQ_TYPE_SIZE\ + LORAMAC_JOIN_EUI_FIELD_SIZE + DEV_NONCE_SIZE + LORAMAC_MHDR_FIELD_SIZE ) #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) #else /* LORAWAN_KMS == 1 */ #define DERIVED_OBJECT_HANDLE_RESET_VAL 0x0UL #define PAYLOAD_MAX_SIZE 270UL /* 270 PHYPayload: 1+(22+1+242)+4 */ #endif /* LORAWAN_KMS */ /* Private macro -------------------------------------------------------------*/ /*! * Hex 8 split buffer */ #define HEX8(X) X[0], X[1], X[2], X[3], X[4], X[5], X[6], X[7] /*! * Hex 16 split buffer */ #define HEX16(X) HEX8(X), X[8], X[9], X[10], X[11], X[12], X[13], X[14], X[15] #if defined (KEY_LOG_ENABLED) && (KEY_LOG_ENABLED == 1) #define KEY_LOG(TS,VL,...) do{ {UTIL_ADV_TRACE_COND_FSend(VL, T_REG_OFF, TS, __VA_ARGS__);} }while(0); #else /* !KEY_LOG_ENABLED */ #define KEY_LOG(TS,VL,...) #endif /* KEY_LOG_ENABLED */ /* Private Types ---------------------------------------------------------*/ /*! * Identifier value pair type for Keys */ typedef struct sKey { /* * Key identifier */ KeyIdentifier_t KeyID; #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) /* * Key value */ uint8_t KeyValue[SE_KEY_SIZE]; #else /* LORAWAN_KMS == 1 */ /* * Key object index in the above list */ CK_OBJECT_HANDLE Object_Index; #endif /* LORAWAN_KMS */ } Key_t; /* * Secure Element Non Volatile Context structure */ typedef struct sSecureElementNvCtx { /* * DevEUI storage */ uint8_t DevEui[SE_EUI_SIZE]; /* * Join EUI storage */ uint8_t JoinEui[SE_EUI_SIZE]; /* * Key List */ Key_t KeyList[NUM_OF_KEYS]; } SecureElementNvCtx_t; /* Private variables ---------------------------------------------------------*/ /*! * Secure element context */ static SecureElementNvCtx_t SeNvmCtx = { /*! * end-device IEEE EUI (big endian) * * \remark In this application the value is automatically generated by calling * BoardGetUniqueId function */ .DevEui = LORAWAN_DEVICE_EUI, /*! * App/Join server IEEE EUI (big endian) */ .JoinEui = LORAWAN_JOIN_EUI }; #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) static const Key_t InitialKeyList[NUM_OF_KEYS] = SOFT_SE_KEY_LIST; #endif /* LORAWAN_KMS == 0 */ static SecureElementNvmEvent SeNvmCtxChanged; #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) #else /* LORAWAN_KMS == 1 */ static CK_ULONG DeriveKey_template_class = CKO_SECRET_KEY; static CK_ULONG DeriveKey_template_destroyable = CK_TRUE; static CK_ULONG DeriveKey_template_encrypt = CK_TRUE; static CK_ULONG DeriveKey_template_decrypt = CK_TRUE; static CK_ULONG DeriveKey_template_extract = CK_FALSE; static CK_ATTRIBUTE DeriveKey_template[] = { {CKA_CLASS, (CK_VOID_PTR) &DeriveKey_template_class, sizeof(CK_ULONG)}, {CKA_DESTROYABLE, (CK_VOID_PTR) &DeriveKey_template_destroyable, sizeof(CK_ULONG)}, {CKA_ENCRYPT, (CK_VOID_PTR) &DeriveKey_template_encrypt, sizeof(CK_ULONG)}, {CKA_DECRYPT, (CK_VOID_PTR) &DeriveKey_template_decrypt, sizeof(CK_ULONG)}, {CKA_EXTRACTABLE, (CK_VOID_PTR) &DeriveKey_template_extract, sizeof(CK_ULONG)} }; /* * Intermediate buffer used for two reasons: * - align to 32 bits and * - for Cmac combine InitVector + input buff */ static uint8_t input_align_combined_buf[PAYLOAD_MAX_SIZE + SE_KEY_SIZE] ALIGN(4); static uint8_t output_align[PAYLOAD_MAX_SIZE] ALIGN(4); static uint8_t tag[SE_KEY_SIZE] ALIGN(4) = {0}; #endif /* LORAWAN_KMS */ /* Private functions prototypes ---------------------------------------------------*/ #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) static SecureElementStatus_t GetKeyByID(KeyIdentifier_t keyID, Key_t **keyItem); #else /* LORAWAN_KMS == 1 */ static SecureElementStatus_t GetKeyIndexByID(KeyIdentifier_t keyID, CK_OBJECT_HANDLE *keyItem); #endif /* LORAWAN_KMS */ static SecureElementStatus_t ComputeCmac(uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t *cmac); static void DummyCB(void); /* Private functions ---------------------------------------------------------*/ #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) /* * Gets key item from key list. * * \param[IN] keyID - Key identifier * \param[OUT] keyItem - Key item reference * \retval - Status of the operation */ static SecureElementStatus_t GetKeyByID(KeyIdentifier_t keyID, Key_t **keyItem) { for (uint8_t i = 0; i < NUM_OF_KEYS; i++) { if (SeNvmCtx.KeyList[i].KeyID == keyID) { *keyItem = &(SeNvmCtx.KeyList[i]); return SECURE_ELEMENT_SUCCESS; } } return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; } #else /* LORAWAN_KMS == 1 */ /* * Gets key Index from key list in KMS table * * \param[IN] keyID - Key identifier * \param[OUT] keyIndex - Key item reference * \retval - Status of the operation */ static SecureElementStatus_t GetKeyIndexByID(KeyIdentifier_t keyID, CK_OBJECT_HANDLE *keyIndex) { for (uint8_t i = 0; i < NUM_OF_KEYS; i++) { if (SeNvmCtx.KeyList[i].KeyID == keyID) { *keyIndex = SeNvmCtx.KeyList[i].Object_Index; return SECURE_ELEMENT_SUCCESS; } } return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; } #endif /* LORAWAN_KMS */ /* * Dummy callback in case if the user provides NULL function pointer */ static void DummyCB(void) { return; } /* * Computes a CMAC of a message using provided initial Bx block * * cmac = aes128_cmac(keyID, blocks[i].Buffer) * * \param[IN] micBxBuffer - Buffer containing the initial Bx block * \param[IN] buffer - Data buffer * \param[IN] size - Data buffer size * \param[IN] keyID - Key identifier to determine the AES key to be used * \param[OUT] cmac - Computed cmac * \retval - Status of the operation */ static SecureElementStatus_t ComputeCmac(uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t *cmac) { SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; if ((buffer == NULL) || (cmac == NULL)) { return SECURE_ELEMENT_ERROR_NPE; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) uint8_t Cmac[16]; AES_CMAC_CTX aesCmacCtx[1]; AES_CMAC_Init(aesCmacCtx); Key_t *keyItem; retval = GetKeyByID(keyID, &keyItem); if (retval == SECURE_ELEMENT_SUCCESS) { AES_CMAC_SetKey(aesCmacCtx, keyItem->KeyValue); if (micBxBuffer != NULL) { AES_CMAC_Update(aesCmacCtx, micBxBuffer, 16); } AES_CMAC_Update(aesCmacCtx, buffer, size); AES_CMAC_Final(Cmac, aesCmacCtx); /* Bring into the required format */ *cmac = (uint32_t)((uint32_t) Cmac[3] << 24 | (uint32_t) Cmac[2] << 16 | (uint32_t) Cmac[1] << 8 | (uint32_t) Cmac[0]); } #else /* LORAWAN_KMS == 1 */ CK_RV rv; CK_SESSION_HANDLE session; CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ uint32_t tag_lenth = 0; CK_OBJECT_HANDLE object_handle ; /* AES CMAC Authentication variables */ CK_MECHANISM aes_cmac_mechanism = { CKM_AES_CMAC, (CK_VOID_PTR)NULL, 0 }; retval = GetKeyIndexByID(keyID, &object_handle); if (retval != SECURE_ELEMENT_SUCCESS) { return retval; } /* Open session with KMS */ rv = C_OpenSession(0, session_flags, NULL, 0, &session); /* Configure session to Authentication message in AES CMAC with settings included into the mechanism */ if (rv == CKR_OK) { rv = C_SignInit(session, &aes_cmac_mechanism, object_handle); } /* Encrypt clear message */ if (rv == CKR_OK) { /* work around : need to double-check if possible to use micBxBuffer as IV for Sign */ if (micBxBuffer != NULL) { memcpy1((uint8_t *) &input_align_combined_buf[0], (uint8_t *) micBxBuffer, SE_KEY_SIZE); memcpy1((uint8_t *) &input_align_combined_buf[SE_KEY_SIZE], (uint8_t *) buffer, size); } else { memcpy1((uint8_t *) &input_align_combined_buf[0], (uint8_t *) buffer, size); } } if (rv == CKR_OK) { if (micBxBuffer != NULL) { rv = C_Sign(session, (CK_BYTE_PTR)&input_align_combined_buf[0], size + SE_KEY_SIZE, &tag[0], (CK_ULONG_PTR)&tag_lenth); } else { rv = C_Sign(session, (CK_BYTE_PTR)&input_align_combined_buf[0], size, &tag[0], (CK_ULONG_PTR)&tag_lenth); } } /* Close session with KMS */ (void)C_CloseSession(session); /* combine to a 32bit authentication word (MIC) */ *cmac = (uint32_t)((uint32_t) tag[3] << 24 | (uint32_t) tag[2] << 16 | (uint32_t) tag[1] << 8 | (uint32_t) tag[0]); if (rv != CKR_OK) { retval = SECURE_ELEMENT_ERROR; } #endif /* LORAWAN_KMS */ return retval; } /* Exported functions ---------------------------------------------------------*/ /* * API functions */ SecureElementStatus_t SecureElementInit(SecureElementNvmEvent seNvmCtxChanged) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) Key_t *keyItem; SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; /* Initialize LoRaWAN Key List buffer */ memcpy1((uint8_t *)(SeNvmCtx.KeyList), (const uint8_t *)InitialKeyList, sizeof(Key_t)*NUM_OF_KEYS); retval = GetKeyByID(APP_KEY, &keyItem); KEY_LOG(TS_OFF, VLEVEL_M, "###### OTAA ######\r\n"); if (retval == SECURE_ELEMENT_SUCCESS) { KEY_LOG(TS_OFF, VLEVEL_M, "###### AppKey: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n", HEX16(keyItem->KeyValue)); } retval = GetKeyByID(NWK_KEY, &keyItem); if (retval == SECURE_ELEMENT_SUCCESS) { KEY_LOG(TS_OFF, VLEVEL_M, "###### NwkKey: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n", HEX16(keyItem->KeyValue)); } KEY_LOG(TS_OFF, VLEVEL_M, "###### ABP ######\r\n"); retval = GetKeyByID(APP_S_KEY, &keyItem); if (retval == SECURE_ELEMENT_SUCCESS) { KEY_LOG(TS_OFF, VLEVEL_M, "###### AppSKey: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n", HEX16(keyItem->KeyValue)); } retval = GetKeyByID(NWK_S_KEY, &keyItem); if (retval == SECURE_ELEMENT_SUCCESS) { KEY_LOG(TS_OFF, VLEVEL_M, "###### NwkSKey: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n", HEX16(keyItem->KeyValue)); } #else /* LORAWAN_KMS == 1 */ uint8_t itr = 0; // Initialize with defaults SeNvmCtx.KeyList[itr++].KeyID = APP_KEY; SeNvmCtx.KeyList[itr++].KeyID = NWK_KEY; #if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) SeNvmCtx.KeyList[itr++].KeyID = J_S_INT_KEY; SeNvmCtx.KeyList[itr++].KeyID = J_S_ENC_KEY; SeNvmCtx.KeyList[itr++].KeyID = F_NWK_S_INT_KEY; SeNvmCtx.KeyList[itr++].KeyID = S_NWK_S_INT_KEY; SeNvmCtx.KeyList[itr++].KeyID = NWK_S_ENC_KEY; #else /* USE_LRWAN_1_1_X_CRYPTO == 0 */ SeNvmCtx.KeyList[itr++].KeyID = NWK_S_KEY; #endif /* USE_LRWAN_1_1_X_CRYPTO */ SeNvmCtx.KeyList[itr++].KeyID = APP_S_KEY; SeNvmCtx.KeyList[itr++].KeyID = MC_ROOT_KEY; SeNvmCtx.KeyList[itr++].KeyID = MC_KE_KEY; SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_0; SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_0; SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_0; #if ( LORAMAC_MAX_MC_CTX > 1 ) SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_1; SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_1; SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_1; SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_2; SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_2; SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_2; SeNvmCtx.KeyList[itr++].KeyID = MC_KEY_3; SeNvmCtx.KeyList[itr++].KeyID = MC_APP_S_KEY_3; SeNvmCtx.KeyList[itr++].KeyID = MC_NWK_S_KEY_3; #endif /*LORAMAC_MAX_MC_CTX > 1 */ SeNvmCtx.KeyList[itr].KeyID = SLOT_RAND_ZERO_KEY; #endif /* LORAWAN_KMS */ /* Assign callback */ if (seNvmCtxChanged != 0) { SeNvmCtxChanged = seNvmCtxChanged; } else { SeNvmCtxChanged = DummyCB; } #if !defined( SECURE_ELEMENT_PRE_PROVISIONED ) #if ( STATIC_DEVICE_EUI == 0 ) /* Get a DevEUI from MCU unique ID */ GetUniqueId(SeNvmCtx.DevEui); #endif /* STATIC_DEVICE_EUI */ #endif /* !SECURE_ELEMENT_PRE_PROVISIONED */ SeNvmCtxChanged(); return SECURE_ELEMENT_SUCCESS; } SecureElementStatus_t SecureElementDeleteDerivedKeys(uint8_t *kms_key_label) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) return SECURE_ELEMENT_ERROR; #else /* LORAWAN_KMS == 1 */ CK_RV rv; CK_SESSION_HANDLE session; CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ /* Open session with KMS */ rv = C_OpenSession(0, session_flags, NULL, 0, &session); /* Work Around: waiting for bugzilla Ticket 80990 */ /* Be sure there are no remaining derived key in the NVM */ /* The problem is that today there is no a Close session procedure */ /* on the other hand this could erase keys generated by the application */ /* so be very careful with this ... */ if (rv == CKR_OK) { rv = C_DestroyObject(session, 50); /* KMS_INDEX_MIN_NVM_DYNAMIC_OBJECTS = 50 */ rv = C_DestroyObject(session, 51); rv = C_DestroyObject(session, 52); rv = C_DestroyObject(session, 53); rv = C_DestroyObject(session, 54); rv = C_DestroyObject(session, 55); rv = C_DestroyObject(session, 56); rv = C_DestroyObject(session, 57); /* from kms_platf_objects_interface.h we use max 8 objects */ } else { return SECURE_ELEMENT_ERROR; } /* Close sessions */ if (session > 0) { (void)C_CloseSession(session); } return SECURE_ELEMENT_SUCCESS; #endif /* LORAWAN_KMS == 1 */ } SecureElementStatus_t SecureElementRestoreNvmCtx(void *seNvmCtx) { /* Restore nvm context */ if (seNvmCtx != 0) { memcpy1((uint8_t *) &SeNvmCtx, (uint8_t *) seNvmCtx, sizeof(SeNvmCtx)); return SECURE_ELEMENT_SUCCESS; } else { return SECURE_ELEMENT_ERROR_NPE; } } void *SecureElementGetNvmCtx(size_t *seNvmCtxSize) { *seNvmCtxSize = sizeof(SeNvmCtx); return &SeNvmCtx; } SecureElementStatus_t SecureElementSetKey(KeyIdentifier_t keyID, uint8_t *key) { if (key == NULL) { return SECURE_ELEMENT_ERROR_NPE; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) for (uint8_t i = 0; i < NUM_OF_KEYS; i++) { if (SeNvmCtx.KeyList[i].KeyID == keyID) { #if ( LORAMAC_MAX_MC_CTX == 1 ) if (keyID == MC_KEY_0) #else /* LORAMAC_MAX_MC_CTX > 1 */ if ((keyID == MC_KEY_0) || (keyID == MC_KEY_1) || (keyID == MC_KEY_2) || (keyID == MC_KEY_3)) #endif /* LORAMAC_MAX_MC_CTX */ { /* Decrypt the key if its a Mckey */ SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; uint8_t decryptedKey[16] = { 0 }; retval = SecureElementAesEncrypt(key, 16, MC_KE_KEY, decryptedKey); memcpy1(SeNvmCtx.KeyList[i].KeyValue, decryptedKey, SE_KEY_SIZE); SeNvmCtxChanged(); return retval; } else { memcpy1(SeNvmCtx.KeyList[i].KeyValue, key, SE_KEY_SIZE); SeNvmCtxChanged(); return SECURE_ELEMENT_SUCCESS; } } } return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; #else /* LORAWAN_KMS == 1 */ /* Indexes are already stored at init or when deriving the key */ CK_OBJECT_HANDLE keyIndex; return GetKeyIndexByID(keyID, &keyIndex); #endif /* LORAWAN_KMS */ } SecureElementStatus_t SecureElementSetObjHandler(KeyIdentifier_t keyID, uint32_t keyIndex) { #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) return SECURE_ELEMENT_ERROR; #else /* LORAWAN_KMS == 1 */ for (uint8_t i = 0; i < NUM_OF_KEYS; i++) { if (SeNvmCtx.KeyList[i].KeyID == keyID) { SeNvmCtx.KeyList[i].Object_Index = (CK_OBJECT_HANDLE) keyIndex; SeNvmCtxChanged(); return SECURE_ELEMENT_SUCCESS; } } return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; #endif /* LORAWAN_KMS */ } SecureElementStatus_t SecureElementComputeAesCmac(uint8_t *micBxBuffer, uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t *cmac) { if (keyID >= LORAMAC_CRYPTO_MULTICAST_KEYS) { /* Never accept multicast key identifier for cmac computation */ return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; } return ComputeCmac(micBxBuffer, buffer, size, keyID, cmac); } SecureElementStatus_t SecureElementVerifyAesCmac(uint8_t *buffer, uint16_t size, uint32_t expectedCmac, KeyIdentifier_t keyID) { SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; if (buffer == NULL) { return SECURE_ELEMENT_ERROR_NPE; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) uint32_t compCmac = 0; retval = ComputeCmac(NULL, buffer, size, keyID, &compCmac); if (retval != SECURE_ELEMENT_SUCCESS) { return retval; } if (expectedCmac != compCmac) { retval = SECURE_ELEMENT_FAIL_CMAC; } #else /* LORAWAN_KMS == 1 */ CK_RV rv; CK_SESSION_HANDLE session; CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ CK_OBJECT_HANDLE object_handle; if (buffer == NULL) { return SECURE_ELEMENT_ERROR_NPE; } /* AES CMAC Authentication variables */ CK_MECHANISM aes_cmac_mechanism = { CKM_AES_CMAC, (CK_VOID_PTR)NULL, 0 }; retval = GetKeyIndexByID(keyID, &object_handle); if (retval != SECURE_ELEMENT_SUCCESS) { return retval; } /* Open session with KMS */ rv = C_OpenSession(0, session_flags, NULL, 0, &session); /* Configure session to Verify the message in AES CMAC with settings included into the mechanism */ if (rv == CKR_OK) { rv = C_VerifyInit(session, &aes_cmac_mechanism, object_handle); } /* Verify the message */ if (rv == CKR_OK) { memcpy1(input_align_combined_buf, buffer, size); rv = C_Verify(session, (CK_BYTE_PTR)input_align_combined_buf, size, (CK_BYTE_PTR)&expectedCmac, 4); } (void)C_CloseSession(session); if (rv != CKR_OK) { retval = SECURE_ELEMENT_ERROR; } #endif /* LORAWAN_KMS */ return retval; } SecureElementStatus_t SecureElementAesEncrypt(uint8_t *buffer, uint16_t size, KeyIdentifier_t keyID, uint8_t *encBuffer) { SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; if (buffer == NULL || encBuffer == NULL) { return SECURE_ELEMENT_ERROR_NPE; } /* Check if the size is divisible by 16 */ if ((size % 16) != 0) { return SECURE_ELEMENT_ERROR_BUF_SIZE; } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) lorawan_aes_context aesContext; memset1(aesContext.ksch, '\0', 240); Key_t *pItem; retval = GetKeyByID(keyID, &pItem); if (retval == SECURE_ELEMENT_SUCCESS) { lorawan_aes_set_key(pItem->KeyValue, 16, &aesContext); uint8_t block = 0; while (size != 0) { lorawan_aes_encrypt(&buffer[block], &encBuffer[block], &aesContext); block = block + 16; size = size - 16; } } #else /* LORAWAN_KMS == 1 */ CK_RV rv; CK_SESSION_HANDLE session; CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ uint32_t encrypted_length = 0; CK_OBJECT_HANDLE object_handle; uint8_t dummy_tag[SE_KEY_SIZE] = {0}; uint32_t dummy_tag_lenth = 0; CK_MECHANISM aes_ecb_mechanism = { CKM_AES_ECB, (CK_VOID_PTR *) NULL, 0 }; retval = GetKeyIndexByID(keyID, &object_handle); if (retval != SECURE_ELEMENT_SUCCESS) { return retval; } /* Open session with KMS */ rv = C_OpenSession(0, session_flags, NULL, 0, &session); /* Configure session to encrypt message in AES ECB with settings included into the mechanism */ if (rv == CKR_OK) { rv = C_EncryptInit(session, &aes_ecb_mechanism, object_handle); } /* Encrypt clear message */ if (rv == CKR_OK) { memcpy1(input_align_combined_buf, buffer, size); encrypted_length = sizeof(output_align); rv = C_EncryptUpdate(session, (CK_BYTE_PTR)input_align_combined_buf, size, output_align, (CK_ULONG_PTR)&encrypted_length); memcpy1(encBuffer, output_align, size); } /* In this case C_EncryptFinal is just called to Free the Alloc mem */ if (rv == CKR_OK) { dummy_tag_lenth = sizeof(tag); rv = C_EncryptFinal(session, &dummy_tag[0], (CK_ULONG_PTR)&dummy_tag_lenth); } /* Close session with KMS */ (void)C_CloseSession(session); if (rv != CKR_OK) { retval = SECURE_ELEMENT_ERROR; } #endif /* LORAWAN_KMS */ return retval; } SecureElementStatus_t SecureElementDeriveAndStoreKey(Version_t version, uint8_t *input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID) { SecureElementStatus_t retval = SECURE_ELEMENT_ERROR; if (input == NULL) { return SECURE_ELEMENT_ERROR_NPE; } /* In case of MC_KE_KEY, only McRootKey can be used as root key */ if (targetKeyID == MC_KE_KEY) { if (rootKeyID != MC_ROOT_KEY) { return SECURE_ELEMENT_ERROR_INVALID_KEY_ID; } } #if (!defined (LORAWAN_KMS) || (LORAWAN_KMS == 0)) uint8_t key[16] = { 0 }; /* Derive key */ retval = SecureElementAesEncrypt(input, 16, rootKeyID, key); if (retval != SECURE_ELEMENT_SUCCESS) { return retval; } /* Store key */ retval = SecureElementSetKey(targetKeyID, key); if (retval != SECURE_ELEMENT_SUCCESS) { return retval; } #else /* LORAWAN_KMS == 1 */ CK_RV rv; CK_SESSION_HANDLE session; CK_FLAGS session_flags = CKF_SERIAL_SESSION; /* Read ONLY session */ /* Key derivation */ CK_MECHANISM mech = {CKM_AES_ECB_ENCRYPT_DATA, input, SE_KEY_SIZE}; CK_OBJECT_HANDLE derivedKeyHdle; CK_OBJECT_HANDLE rootkey_object_handle; /* Derive key */ retval = GetKeyIndexByID(rootKeyID, &rootkey_object_handle); if (retval != SECURE_ELEMENT_SUCCESS) { return retval; } /* Open session with KMS */ rv = C_OpenSession(0, session_flags, NULL, 0, &session); /* Derive key with pass phrase */ if (rv == CKR_OK) { rv = C_DeriveKey(session, &(mech), rootkey_object_handle, &DeriveKey_template[0], sizeof(DeriveKey_template) / sizeof(CK_ATTRIBUTE), &derivedKeyHdle); } if (rv == CKR_OK) { /* Store Derived Index in table */ retval = SecureElementSetObjHandler(targetKeyID, derivedKeyHdle); } /* Close session with KMS */ (void)C_CloseSession(session); if (rv != CKR_OK) { retval = SECURE_ELEMENT_ERROR; } #endif /* LORAWAN_KMS */ return retval; } SecureElementStatus_t SecureElementProcessJoinAccept(JoinReqIdentifier_t joinReqType, uint8_t *joinEui, uint16_t devNonce, uint8_t *encJoinAccept, uint8_t encJoinAcceptSize, uint8_t *decJoinAccept, uint8_t *versionMinor) { if ((encJoinAccept == NULL) || (decJoinAccept == NULL) || (versionMinor == NULL)) { return SECURE_ELEMENT_ERROR_NPE; } /* Check that frame size isn't bigger than a JoinAccept with CFList size */ if (encJoinAcceptSize > LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE) { return SECURE_ELEMENT_ERROR_BUF_SIZE; } /* Determine decryption key */ KeyIdentifier_t encKeyID = NWK_KEY; #if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) if (joinReqType != JOIN_REQ) { encKeyID = J_S_ENC_KEY; } #endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ memcpy1(decJoinAccept, encJoinAccept, encJoinAcceptSize); /* Decrypt JoinAccept, skip MHDR */ if (SecureElementAesEncrypt(encJoinAccept + LORAMAC_MHDR_FIELD_SIZE, encJoinAcceptSize - LORAMAC_MHDR_FIELD_SIZE, encKeyID, decJoinAccept + LORAMAC_MHDR_FIELD_SIZE) != SECURE_ELEMENT_SUCCESS) { return SECURE_ELEMENT_FAIL_ENCRYPT; } *versionMinor = ((decJoinAccept[11] & 0x80) == 0x80) ? 1 : 0; uint32_t mic = 0; mic = ((uint32_t) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE] << 0); mic |= ((uint32_t) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 1] << 8); mic |= ((uint32_t) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 2] << 16); mic |= ((uint32_t) decJoinAccept[encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE + 3] << 24); /* - Header buffer to be used for MIC computation * - LoRaWAN 1.0.x : micHeader = [MHDR(1)] * - LoRaWAN 1.1.x : micHeader = [JoinReqType(1), JoinEUI(8), DevNonce(2), MHDR(1)] */ /* Verify mic */ if (*versionMinor == 0) { /* For LoRaWAN 1.0.x * cmac = aes128_cmac(NwkKey, MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | * CFListType) */ if (SecureElementVerifyAesCmac(decJoinAccept, (encJoinAcceptSize - LORAMAC_MIC_FIELD_SIZE), mic, NWK_KEY) != SECURE_ELEMENT_SUCCESS) { return SECURE_ELEMENT_FAIL_CMAC; } } #if ( USE_LRWAN_1_1_X_CRYPTO == 1 ) else if (*versionMinor == 1) { uint8_t micHeader11[JOIN_ACCEPT_MIC_COMPUTATION_OFFSET] = { 0 }; uint16_t bufItr = 0; micHeader11[bufItr++] = (uint8_t) joinReqType; memcpyr(micHeader11 + bufItr, joinEui, LORAMAC_JOIN_EUI_FIELD_SIZE); bufItr += LORAMAC_JOIN_EUI_FIELD_SIZE; micHeader11[bufItr++] = devNonce & 0xFF; micHeader11[bufItr++] = (devNonce >> 8) & 0xFF; /* For LoRaWAN 1.1.x and later: * cmac = aes128_cmac(JSIntKey, JoinReqType | JoinEUI | DevNonce | MHDR | JoinNonce | NetID | DevAddr | * DLSettings | RxDelay | CFList | CFListType) * Prepare the msg for integrity check (adding JoinReqType, JoinEUI and DevNonce) */ uint8_t localBuffer[LORAMAC_JOIN_ACCEPT_FRAME_MAX_SIZE + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET] = { 0 }; memcpy1(localBuffer, micHeader11, JOIN_ACCEPT_MIC_COMPUTATION_OFFSET); memcpy1(localBuffer + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET - 1, decJoinAccept, encJoinAcceptSize); if (SecureElementVerifyAesCmac(localBuffer, encJoinAcceptSize + JOIN_ACCEPT_MIC_COMPUTATION_OFFSET - LORAMAC_MHDR_FIELD_SIZE - LORAMAC_MIC_FIELD_SIZE, mic, J_S_INT_KEY) != SECURE_ELEMENT_SUCCESS) { return SECURE_ELEMENT_FAIL_CMAC; } } #endif /* USE_LRWAN_1_1_X_CRYPTO == 1 */ else { return SECURE_ELEMENT_ERROR_INVALID_LORAWAM_SPEC_VERSION; } return SECURE_ELEMENT_SUCCESS; } SecureElementStatus_t SecureElementRandomNumber(uint32_t *randomNum) { if (randomNum == NULL) { return SECURE_ELEMENT_ERROR_NPE; } *randomNum = Radio.Random( ); return SECURE_ELEMENT_SUCCESS; } SecureElementStatus_t SecureElementSetDevEui(uint8_t *devEui) { if (devEui == NULL) { return SECURE_ELEMENT_ERROR_NPE; } memcpy1(SeNvmCtx.DevEui, devEui, SE_EUI_SIZE); SeNvmCtxChanged(); return SECURE_ELEMENT_SUCCESS; } uint8_t *SecureElementGetDevEui(void) { return SeNvmCtx.DevEui; } SecureElementStatus_t SecureElementSetJoinEui(uint8_t *joinEui) { if (joinEui == NULL) { return SECURE_ELEMENT_ERROR_NPE; } memcpy1(SeNvmCtx.JoinEui, joinEui, SE_EUI_SIZE); SeNvmCtxChanged(); return SECURE_ELEMENT_SUCCESS; } uint8_t *SecureElementGetJoinEui(void) { return SeNvmCtx.JoinEui; } /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/