
fix Security breach found by Tencent Blade Team please refer to security advisory - CVE-2020-11068
538 lines
14 KiB
C
538 lines
14 KiB
C
/*!
|
|
* \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
|
|
*
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
|
|
#include "utilities.h"
|
|
#include "aes.h"
|
|
#include "cmac.h"
|
|
|
|
#include "LoRaMacHeaderTypes.h"
|
|
|
|
#include "secure-element.h"
|
|
#include "se-identity.h"
|
|
#include "soft-se-hal.h"
|
|
|
|
/*!
|
|
* Number of supported crypto keys
|
|
*/
|
|
#define NUM_OF_KEYS 23
|
|
|
|
/*!
|
|
* Identifier value pair type for Keys
|
|
*/
|
|
typedef struct sKey
|
|
{
|
|
/*
|
|
* Key identifier
|
|
*/
|
|
KeyIdentifier_t KeyID;
|
|
/*
|
|
* Key value
|
|
*/
|
|
uint8_t KeyValue[SE_KEY_SIZE];
|
|
} 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];
|
|
/*
|
|
* Pin storage
|
|
*/
|
|
uint8_t Pin[SE_PIN_SIZE];
|
|
/*
|
|
* Key List
|
|
*/
|
|
Key_t KeyList[NUM_OF_KEYS];
|
|
} SecureElementNvCtx_t;
|
|
|
|
/*!
|
|
* 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,
|
|
/*!
|
|
* Secure-element pin (big endian)
|
|
*/
|
|
.Pin = SECURE_ELEMENT_PIN,
|
|
/*!
|
|
* LoRaWAN key list
|
|
*/
|
|
.KeyList = SOFT_SE_KEY_LIST
|
|
};
|
|
|
|
static SecureElementNvmEvent SeNvmCtxChanged;
|
|
|
|
/*
|
|
* Local functions
|
|
*/
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* 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 )
|
|
{
|
|
if( ( buffer == NULL ) || ( cmac == NULL ) )
|
|
{
|
|
return SECURE_ELEMENT_ERROR_NPE;
|
|
}
|
|
|
|
uint8_t Cmac[16];
|
|
AES_CMAC_CTX aesCmacCtx[1];
|
|
|
|
AES_CMAC_Init( aesCmacCtx );
|
|
|
|
Key_t* keyItem;
|
|
SecureElementStatus_t 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] );
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
* API functions
|
|
*/
|
|
|
|
SecureElementStatus_t SecureElementInit( SecureElementNvmEvent seNvmCtxChanged )
|
|
{
|
|
// 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
|
|
SoftSeHalGetUniqueId( SeNvmCtx.DevEui );
|
|
#endif
|
|
#endif
|
|
|
|
SeNvmCtxChanged( );
|
|
|
|
return SECURE_ELEMENT_SUCCESS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
for( uint8_t i = 0; i < NUM_OF_KEYS; i++ )
|
|
{
|
|
if( SeNvmCtx.KeyList[i].KeyID == keyID )
|
|
{
|
|
if( ( keyID == MC_KEY_0 ) || ( keyID == MC_KEY_1 ) || ( keyID == MC_KEY_2 ) || ( keyID == MC_KEY_3 ) )
|
|
{ // 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;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
if( buffer == NULL )
|
|
{
|
|
return SECURE_ELEMENT_ERROR_NPE;
|
|
}
|
|
|
|
SecureElementStatus_t retval = SECURE_ELEMENT_ERROR;
|
|
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;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID,
|
|
uint8_t* encBuffer )
|
|
{
|
|
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;
|
|
}
|
|
|
|
aes_context aesContext;
|
|
memset1( aesContext.ksch, '\0', 240 );
|
|
|
|
Key_t* pItem;
|
|
SecureElementStatus_t retval = GetKeyByID( keyID, &pItem );
|
|
|
|
if( retval == SECURE_ELEMENT_SUCCESS )
|
|
{
|
|
aes_set_key( pItem->KeyValue, 16, &aesContext );
|
|
|
|
uint8_t block = 0;
|
|
|
|
while( size != 0 )
|
|
{
|
|
aes_encrypt( &buffer[block], &encBuffer[block], &aesContext );
|
|
block = block + 16;
|
|
size = size - 16;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
SecureElementStatus_t SecureElementDeriveAndStoreKey( Version_t version, uint8_t* input, KeyIdentifier_t rootKeyID,
|
|
KeyIdentifier_t targetKeyID )
|
|
{
|
|
if( input == NULL )
|
|
{
|
|
return SECURE_ELEMENT_ERROR_NPE;
|
|
}
|
|
|
|
SecureElementStatus_t retval = SECURE_ELEMENT_ERROR;
|
|
uint8_t key[16] = { 0 };
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
return SECURE_ELEMENT_SUCCESS;
|
|
}
|
|
|
|
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( joinReqType != JOIN_REQ )
|
|
{
|
|
encKeyID = J_S_ENC_KEY;
|
|
}
|
|
|
|
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
|
|
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 = SoftSeHalGetRandomNumber( );
|
|
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;
|
|
}
|
|
|
|
SecureElementStatus_t SecureElementSetPin( uint8_t* pin )
|
|
{
|
|
if( pin == NULL )
|
|
{
|
|
return SECURE_ELEMENT_ERROR_NPE;
|
|
}
|
|
|
|
memcpy1( SeNvmCtx.Pin, pin, SE_PIN_SIZE );
|
|
SeNvmCtxChanged( );
|
|
return SECURE_ELEMENT_SUCCESS;
|
|
}
|
|
|
|
uint8_t* SecureElementGetPin( void )
|
|
{
|
|
return SeNvmCtx.Pin;
|
|
}
|