/*! * \file lr1110-se.c * * \brief LR1110 Secure Element hardware implementation * * \copyright Revised BSD License, see section \ref LICENSE. * * \code * ______ _ * / _____) _ | | * ( (____ _____ ____ _| |_ _____ ____| |__ * \____ \| ___ | (_ _) ___ |/ ___) _ \ * _____) ) ____| | | || |_| ____( (___| | | | * (______/|_____)_|_|_| \__)_____)\____)_| |_| * (C)2019-2019 Semtech * * \endcode * * \authors Semtech WSP Applications Team */ #include #include #include "lr1110.h" #include "lr1110_system.h" #include "lr1110_crypto_engine.h" #include "secure-element.h" #include "secure-element-nvm.h" #include "se-identity.h" #include "lr1110-se-hal.h" /*! * Number of supported crypto keys */ #define NUM_OF_KEYS 23 /* * CMAC/AES Message Integrity Code (MIC) Block B0 size */ #define MIC_BLOCK_BX_SIZE 16 /* * Maximum size of the message that can be handled by the crypto operations */ #define CRYPTO_MAXMESSAGE_SIZE 256 /* * Maximum size of the buffer for crypto operations */ #define CRYPTO_BUFFER_SIZE CRYPTO_MAXMESSAGE_SIZE + MIC_BLOCK_BX_SIZE static SecureElementNvmData_t* SeNvm; /*! * LR1110 radio context */ extern lr1110_t LR1110; /*! * Converts key ids from SecureElement to LR1110 * * \param [IN] key_id SecureElement key id to be converted * * \retval key_id Converted LR1110 key id */ static lr1110_crypto_keys_idx_t convert_key_id_from_se_to_lr1110( KeyIdentifier_t key_id ); SecureElementStatus_t SecureElementInit( SecureElementNvmData_t* nvm ) { lr1110_crypto_status_t status = LR1110_CRYPTO_STATUS_ERROR; SecureElementNvmData_t seNvmInit = { /*! * 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, }; // Initialize nvm pointer SeNvm = nvm; // Initialize data memcpy1( ( uint8_t* )SeNvm, ( uint8_t* )&seNvmInit, sizeof( seNvmInit ) ); lr1110_crypto_restore_from_flash( &LR1110, &status ); if( status != LR1110_CRYPTO_STATUS_SUCCESS ) { return ( SecureElementStatus_t ) status; } #if defined( SECURE_ELEMENT_PRE_PROVISIONED ) // Read LR1110 pre-provisioned identity lr1110_system_read_uid( &LR1110, SeNvm->DevEui ); lr1110_system_read_join_eui( &LR1110, SeNvm->JoinEui ); lr1110_system_read_pin( &LR1110, SeNvm->Pin ); #else #if( STATIC_DEVICE_EUI == 0 ) // Get a DevEUI from MCU unique ID LR1110SeHalGetUniqueId( SeNvm->DevEui ); #endif #endif const lr1110_crypto_key_t zero_key = { 0 }; lr1110_crypto_set_key( &LR1110, &status, convert_key_id_from_se_to_lr1110( SLOT_RAND_ZERO_KEY ), zero_key ); return ( SecureElementStatus_t ) status; } SecureElementStatus_t SecureElementSetKey( KeyIdentifier_t keyID, uint8_t* key ) { if( key == NULL ) { return SECURE_ELEMENT_ERROR_NPE; } SecureElementStatus_t status = SECURE_ELEMENT_ERROR; if( ( keyID == MC_KEY_0 ) || ( keyID == MC_KEY_1 ) || ( keyID == MC_KEY_2 ) || ( keyID == MC_KEY_3 ) ) { // Decrypt the key if its a Mckey lr1110_crypto_derive_and_store_key( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( MC_KE_KEY ), convert_key_id_from_se_to_lr1110( keyID ), key ); if( status == SECURE_ELEMENT_SUCCESS ) { lr1110_crypto_store_to_flash( &LR1110, ( lr1110_crypto_status_t* ) &status ); } return status; } else { lr1110_crypto_set_key( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( keyID ), key ); if( status == SECURE_ELEMENT_SUCCESS ) { lr1110_crypto_store_to_flash( &LR1110, ( lr1110_crypto_status_t* ) &status ); } return status; } } SecureElementStatus_t SecureElementComputeAesCmac( uint8_t* micBxBuffer, uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint32_t* cmac ) { SecureElementStatus_t status = SECURE_ELEMENT_ERROR; uint16_t localSize = size; uint8_t* localbuffer = buffer; if( micBxBuffer != NULL ) { uint8_t micBuff[CRYPTO_BUFFER_SIZE]; memset1( micBuff, 0, CRYPTO_BUFFER_SIZE ); memcpy1( micBuff, micBxBuffer, MIC_BLOCK_BX_SIZE ); memcpy1( ( micBuff + MIC_BLOCK_BX_SIZE ), buffer, size ); localSize += MIC_BLOCK_BX_SIZE; localbuffer = micBuff; } lr1110_crypto_compute_aes_cmac( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( keyID ), localbuffer, localSize, ( uint8_t* ) cmac ); return status; } SecureElementStatus_t SecureElementVerifyAesCmac( uint8_t* buffer, uint16_t size, uint32_t expectedCmac, KeyIdentifier_t keyID ) { SecureElementStatus_t status = SECURE_ELEMENT_ERROR; if( buffer == NULL ) { return SECURE_ELEMENT_ERROR_NPE; } lr1110_crypto_verify_aes_cmac( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( keyID ), buffer, size, ( uint8_t* ) &expectedCmac ); return status; } SecureElementStatus_t SecureElementAesEncrypt( uint8_t* buffer, uint16_t size, KeyIdentifier_t keyID, uint8_t* encBuffer ) { SecureElementStatus_t status = SECURE_ELEMENT_ERROR; if( ( buffer == NULL ) || ( encBuffer == NULL ) ) { return SECURE_ELEMENT_ERROR_NPE; } if( keyID < SLOT_RAND_ZERO_KEY ) { lr1110_crypto_aes_encrypt_01( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( keyID ), buffer, size, encBuffer ); } else { lr1110_crypto_aes_encrypt( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( keyID ), buffer, size, encBuffer ); } return status; } SecureElementStatus_t SecureElementDeriveAndStoreKey( uint8_t* input, KeyIdentifier_t rootKeyID, KeyIdentifier_t targetKeyID ) { SecureElementStatus_t status = SECURE_ELEMENT_ERROR; if( input == NULL ) { return SECURE_ELEMENT_ERROR_NPE; } lr1110_crypto_derive_and_store_key( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( rootKeyID ), convert_key_id_from_se_to_lr1110( targetKeyID ), input ); lr1110_crypto_store_to_flash( &LR1110, ( lr1110_crypto_status_t* ) &status ); return status; } SecureElementStatus_t SecureElementProcessJoinAccept( JoinReqIdentifier_t joinReqType, uint8_t* joinEui, uint16_t devNonce, uint8_t* encJoinAccept, uint8_t encJoinAcceptSize, uint8_t* decJoinAccept, uint8_t* versionMinor ) { SecureElementStatus_t status = SECURE_ELEMENT_ERROR; 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; } // - 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)] // Try first to process LoRaWAN 1.0.x JoinAccept uint8_t micHeader10[1] = { 0x20 }; // cmac = aes128_cmac(NwkKey, MHDR | JoinNonce | NetID | DevAddr | DLSettings | RxDelay | CFList | // CFListType) lr1110_crypto_process_join_accept( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( encKeyID ), convert_key_id_from_se_to_lr1110( NWK_KEY ), ( lr1110_crypto_lorawan_version_t ) 0, micHeader10, encJoinAccept + 1, encJoinAcceptSize - 1, decJoinAccept + 1 ); if( status == SECURE_ELEMENT_SUCCESS ) { *versionMinor = ( ( decJoinAccept[11] & 0x80 ) == 0x80 ) ? 1 : 0; if( *versionMinor == 0 ) { // Network server is operating according to LoRaWAN 1.0.x return SECURE_ELEMENT_SUCCESS; } } #if( USE_LRWAN_1_1_X_CRYPTO == 1 ) // 1.0.x trial failed. Trying to process LoRaWAN 1.1.x JoinAccept uint8_t micHeader11[JOIN_ACCEPT_MIC_COMPUTATION_OFFSET] = { 0 }; uint16_t bufItr = 0; // cmac = aes128_cmac(JSIntKey, JoinReqType | JoinEUI | DevNonce | MHDR | JoinNonce | NetID | DevAddr | // DLSettings | RxDelay | CFList | CFListType) 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; micHeader11[bufItr++] = 0x20; lr1110_crypto_process_join_accept( &LR1110, ( lr1110_crypto_status_t* ) &status, convert_key_id_from_se_to_lr1110( encKeyID ), convert_key_id_from_se_to_lr1110( J_S_INT_KEY ), ( lr1110_crypto_lorawan_version_t ) 1, micHeader11, encJoinAccept + 1, encJoinAcceptSize - 1, decJoinAccept + 1 ); if( status == SECURE_ELEMENT_SUCCESS ) { *versionMinor = ( ( decJoinAccept[11] & 0x80 ) == 0x80 ) ? 1 : 0; if( *versionMinor == 1 ) { // Network server is operating according to LoRaWAN 1.1.x return SECURE_ELEMENT_SUCCESS; } } #endif return status; } SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum ) { if( randomNum == NULL ) { return SECURE_ELEMENT_ERROR_NPE; } *randomNum = LR1110SeHalGetRandomNumber( ); return SECURE_ELEMENT_SUCCESS; } SecureElementStatus_t SecureElementSetDevEui( uint8_t* devEui ) { if( devEui == NULL ) { return SECURE_ELEMENT_ERROR_NPE; } memcpy1( SeNvm->DevEui, devEui, SE_EUI_SIZE ); return SECURE_ELEMENT_SUCCESS; } uint8_t* SecureElementGetDevEui( void ) { return SeNvm->DevEui; } SecureElementStatus_t SecureElementSetJoinEui( uint8_t* joinEui ) { if( joinEui == NULL ) { return SECURE_ELEMENT_ERROR_NPE; } memcpy1( SeNvm->JoinEui, joinEui, SE_EUI_SIZE ); return SECURE_ELEMENT_SUCCESS; } uint8_t* SecureElementGetJoinEui( void ) { return SeNvm->JoinEui; } SecureElementStatus_t SecureElementSetPin( uint8_t* pin ) { if( pin == NULL ) { return SECURE_ELEMENT_ERROR_NPE; } memcpy1( SeNvm->Pin, pin, SE_PIN_SIZE ); return SECURE_ELEMENT_SUCCESS; } uint8_t* SecureElementGetPin( void ) { return SeNvm->Pin; } static lr1110_crypto_keys_idx_t convert_key_id_from_se_to_lr1110( KeyIdentifier_t key_id ) { lr1110_crypto_keys_idx_t id = LR1110_CRYPTO_KEYS_IDX_GP0; switch( key_id ) { case APP_KEY: id = LR1110_CRYPTO_KEYS_IDX_APP_KEY; break; case NWK_KEY: id = LR1110_CRYPTO_KEYS_IDX_NWK_KEY; break; case J_S_INT_KEY: id = LR1110_CRYPTO_KEYS_IDX_J_S_INT_KEY; break; case J_S_ENC_KEY: id = LR1110_CRYPTO_KEYS_IDX_J_S_ENC_KEY; break; case F_NWK_S_INT_KEY: id = LR1110_CRYPTO_KEYS_IDX_F_NWK_S_INT_KEY; break; case S_NWK_S_INT_KEY: id = LR1110_CRYPTO_KEYS_IDX_S_NWK_S_INT_KEY; break; case NWK_S_ENC_KEY: id = LR1110_CRYPTO_KEYS_IDX_NWK_S_ENC_KEY; break; case APP_S_KEY: id = LR1110_CRYPTO_KEYS_IDX_APP_S_KEY; break; case MC_ROOT_KEY: id = LR1110_CRYPTO_KEYS_IDX_GP_KE_KEY_5; break; case MC_KE_KEY: id = LR1110_CRYPTO_KEYS_IDX_GP_KE_KEY_4; break; case MC_KEY_0: id = LR1110_CRYPTO_KEYS_IDX_GP_KE_KEY_0; break; case MC_APP_S_KEY_0: id = LR1110_CRYPTO_KEYS_IDX_MC_APP_S_KEY_0; break; case MC_NWK_S_KEY_0: id = LR1110_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_0; break; case MC_KEY_1: id = LR1110_CRYPTO_KEYS_IDX_GP_KE_KEY_1; break; case MC_APP_S_KEY_1: id = LR1110_CRYPTO_KEYS_IDX_MC_APP_S_KEY_1; break; case MC_NWK_S_KEY_1: id = LR1110_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_1; break; case MC_KEY_2: id = LR1110_CRYPTO_KEYS_IDX_GP_KE_KEY_2; break; case MC_APP_S_KEY_2: id = LR1110_CRYPTO_KEYS_IDX_MC_APP_S_KEY_2; break; case MC_NWK_S_KEY_2: id = LR1110_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_2; break; case MC_KEY_3: id = LR1110_CRYPTO_KEYS_IDX_GP_KE_KEY_3; break; case MC_APP_S_KEY_3: id = LR1110_CRYPTO_KEYS_IDX_MC_APP_S_KEY_3; break; case MC_NWK_S_KEY_3: id = LR1110_CRYPTO_KEYS_IDX_MC_NWK_S_KEY_3; break; case SLOT_RAND_ZERO_KEY: id = LR1110_CRYPTO_KEYS_IDX_GP0; break; default: id = LR1110_CRYPTO_KEYS_IDX_GP1; break; } return id; }