/****************************************************************************** * * Copyright 2018 NXP * * 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. * ******************************************************************************/ #define LOG_TAG "LSClient" #include #include #include #include #include #include #include #include #include #include #include "LsClient.h" #include "LsLib.h" uint8_t datahex(char c); unsigned char* getHASH(uint8_t* buffer, size_t buffSize); extern bool ese_debug_enabled; #define ls_script_source_prefix "/vendor/etc/loaderservice_updater_" #define ls_script_source_suffix ".lss" #define ls_script_output_prefix \ "/data/vendor/secure_element/loaderservice_updater_out_" #define ls_script_output_suffix ".txt" const size_t HASH_DATA_LENGTH = 21; const uint16_t HASH_STATUS_INDEX = 20; const uint8_t LS_MAX_COUNT = 10; const uint8_t LS_DOWNLOAD_SUCCESS = 0x00; const uint8_t LS_DOWNLOAD_FAILED = 0x01; class LSInfo { public: uint8_t m_status; uint8_t m_version; uint8_t m_mode; uint8_t m_slot1_status; uint8_t m_slot1_hash; uint8_t m_slot2_status; uint8_t m_slot2_hash; }; static LSC_onCompletedCallback mCallback = nullptr; static void* mCallbackParams = NULL; void* performLSDownload_thread(void* data); static void getLSScriptSourcePrefix(std::string& prefix); static int compareLSHash(uint8_t* hash, uint8_t length); static std::string printLSStatus(int status); static std::string dumpLsInfo(LSInfo* info); static int compareLSHash(uint8_t* hash, uint8_t length) { uint8_t ls253UpdaterScriptHash[HASH_DATA_LENGTH - 1] = { 0x65, 0x80, 0xFB, 0xA0, 0xCA, 0x59, 0xAE, 0x6C, 0x71, 0x6B, 0x15, 0xB1, 0xBD, 0xB1, 0x2C, 0x04, 0x29, 0x14, 0x8A, 0x8F}; uint8_t ls253AppletScriptHash[HASH_DATA_LENGTH - 1] = { 0x71, 0x7B, 0x8D, 0x0C, 0xEA, 0xE7, 0xEC, 0xC1, 0xCF, 0x47, 0x33, 0x10, 0xFE, 0x8E, 0x52, 0x5D, 0xB1, 0x43, 0x9B, 0xDE}; uint8_t lsFactoryScript1Hash[HASH_DATA_LENGTH - 1] = { 0x4A, 0xD0, 0x37, 0xD0, 0x44, 0x5B, 0x78, 0x55, 0x17, 0x5E, 0xFD, 0x87, 0x9C, 0xF1, 0x74, 0xBA, 0x77, 0xAD, 0x03, 0x62}; uint8_t lsFactoryScript2Hash[HASH_DATA_LENGTH - 1] = { 0xA9, 0xDB, 0x03, 0x53, 0xC2, 0xD7, 0xF8, 0xFC, 0x84, 0x37, 0xAF, 0xB9, 0x53, 0x06, 0x27, 0x9D, 0xE9, 0x68, 0x45, 0xEF}; uint8_t lsFactoryScript3Hash[HASH_DATA_LENGTH - 1] = { 0xA9, 0xAE, 0x5E, 0x66, 0x92, 0x8F, 0x70, 0xBD, 0x0A, 0xC7, 0x20, 0x8A, 0x6A, 0xBB, 0x63, 0xB3, 0xCA, 0x05, 0x58, 0xC1}; uint8_t lsFactoryScript4Hash[HASH_DATA_LENGTH - 1] = { 0x64, 0x73, 0x56, 0xAE, 0x58, 0x27, 0x6C, 0x07, 0x4B, 0xBA, 0x64, 0x7E, 0x6E, 0xC1, 0x97, 0xC8, 0x57, 0x17, 0x6E, 0x2D}; uint8_t* hashList[6] = {lsFactoryScript1Hash, lsFactoryScript2Hash, lsFactoryScript3Hash, lsFactoryScript4Hash, ls253UpdaterScriptHash, ls253AppletScriptHash}; if (length != HASH_DATA_LENGTH - 1) { return 0xFF; } for (int i = 0; i < 6; i++) { if (0 == memcmp(hash, hashList[i], length)) { return i + 1; } } return 0xFF; } static std::string dumpLsInfo(LSInfo* info) { std::stringstream buff; buff << std::setw(2) << std::setfill('0') << std::hex << (int)(info->m_status); buff << std::setw(2) << std::setfill('0') << std::hex << (int)(info->m_version); buff << std::setw(2) << std::setfill('0') << std::hex << (int)(info->m_mode); buff << std::setw(2) << std::setfill('0') << std::hex << (int)(info->m_slot1_status); buff << std::setw(2) << std::setfill('0') << std::hex << (int)(info->m_slot1_hash); buff << std::setw(2) << std::setfill('0') << std::hex << (int)(info->m_slot2_status); buff << std::setw(2) << std::setfill('0') << std::hex << (int)(info->m_slot2_hash); return buff.str(); } void getLSScriptSourcePrefix(std::string& prefix) { char source_path[PROPERTY_VALUE_MAX] = {0}; int len = property_get("vendor.ese.loader_script_path", source_path, ""); if (len > 0) { FILE* fd = fopen(source_path, "rb"); if (fd != NULL) { char c; while (!feof(fd) && fread(&c, 1, 1, fd) == 1) { if (c == ' ' || c == '\n' || c == '\r' || c == 0x00) break; prefix.push_back(c); } fclose(fd); } else { ALOGD("%s Cannot open file %s\n", __func__, source_path); } } if (prefix.empty()) { prefix.assign(ls_script_source_prefix); } } /******************************************************************************* ** ** Function: LSC_Start ** ** Description: Starts the LSC update with encrypted data privided in the updater file ** ** Returns: SUCCESS if ok. ** *******************************************************************************/ LSCSTATUS LSC_Start(const char* name, const char* dest, uint8_t* pdata, uint16_t len, uint8_t* respSW) { static const char fn[] = "LSC_Start"; LSCSTATUS status = LSCSTATUS_FAILED; if (name != NULL) { status = Perform_LSC(name, dest, pdata, len, respSW); } else { ALOGE("%s: LS script file is missing", fn); } ALOGD_IF(ese_debug_enabled, "%s: Exit; status=0x0%X", fn, status); return status; } /******************************************************************************* ** ** Function: LSC_doDownload ** ** Description: Start LS download process ** ** Returns: SUCCESS if ok ** *******************************************************************************/ LSCSTATUS LSC_doDownload(LSC_onCompletedCallback callback, void* args) { static const char fn[] = "LSC_doDownload"; mCallback = callback; mCallbackParams = args; LSCSTATUS status; pthread_t thread; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (pthread_create(&thread, &attr, &performLSDownload_thread, NULL) < 0) { ALOGE("%s: Thread creation failed", fn); status = LSCSTATUS_FAILED; } else { status = LSCSTATUS_SUCCESS; } pthread_attr_destroy(&attr); return status; } /******************************************************************************* ** ** Function: printLSStatus ** ** Description: print LS applet state and Slot 1 & 2 data ** ** Returns: LS status log ** *******************************************************************************/ std::string printLSStatus(int lsStatus) { ALOGD_IF(ese_debug_enabled, "%s enter ", __func__); uint8_t slotHashBuffer[HASH_DATA_LENGTH] = {0}; uint16_t length = 0; uint16_t lsVersion = 0; uint8_t lsMode = 0; std::stringstream outStream; std::stringstream outHash; LSInfo lsInfo; memset(&lsInfo, 0xFF, sizeof(LSInfo)); lsInfo.m_status = lsStatus; outStream << "\nCurrent LS info:"; /*Read LS applet mode*/ LSCSTATUS status = LSC_ReadLscInfo(&lsMode, &lsVersion); if (status != LSCSTATUS_SUCCESS) { outStream << dumpLsInfo(&lsInfo); outStream << "\nFailed to access LS applet!\n"; return outStream.str(); } ALOGI_IF(ese_debug_enabled, "LS applet version is %d.%d", (lsVersion >> 8), (lsVersion & 0xFF)); if (lsMode == 2) { ALOGI_IF(ese_debug_enabled, "LS is in UPDATE mode!"); } lsInfo.m_version = lsVersion & 0xFF; lsInfo.m_mode = lsMode; /*Read the hash from slot 1*/ status = LSC_ReadLsHash(slotHashBuffer, &length, 1); if (status != LSCSTATUS_SUCCESS) { ALOGI_IF(ese_debug_enabled, "Failed to read Hash value from slot 1."); outStream << dumpLsInfo(&lsInfo); return outStream.str(); } if (slotHashBuffer[HASH_DATA_LENGTH - 1] == LS_DOWNLOAD_SUCCESS) { ALOGI_IF(ese_debug_enabled, "LS Slot 1 passed."); lsInfo.m_slot1_status = LS_DOWNLOAD_SUCCESS; } else { ALOGI_IF(ese_debug_enabled, "LS Slot 1 failed."); lsInfo.m_slot1_status = LS_DOWNLOAD_FAILED; } lsInfo.m_slot1_hash = compareLSHash(slotHashBuffer, HASH_DATA_LENGTH - 1); if (lsInfo.m_slot1_hash == 0xFF) { outHash << "\n slot 1 hash:\n"; for (int i = 0; i < HASH_DATA_LENGTH - 1; i++) { outHash << std::setw(2) << std::setfill('0') << std::hex << (int)slotHashBuffer[i]; } } /*Read the hash from slot 2*/ status = LSC_ReadLsHash(slotHashBuffer, &length, 2); if (status != LSCSTATUS_SUCCESS) { ALOGI_IF(ese_debug_enabled, "Failed to read Hash value from slot 1."); outStream << dumpLsInfo(&lsInfo); return outStream.str(); } if (slotHashBuffer[HASH_DATA_LENGTH - 1] == LS_DOWNLOAD_SUCCESS) { ALOGI_IF(ese_debug_enabled, "LS Slot 2 passed."); lsInfo.m_slot2_status = LS_DOWNLOAD_SUCCESS; } else { ALOGI_IF(ese_debug_enabled, "LS Slot 2 failed."); lsInfo.m_slot2_status = LS_DOWNLOAD_FAILED; } lsInfo.m_slot2_hash = compareLSHash(slotHashBuffer, HASH_DATA_LENGTH - 1); if (lsInfo.m_slot2_hash == 0xFF) { outHash << "\n slot 2 hash:\n"; for (int i = 0; i < HASH_DATA_LENGTH - 1; i++) { outHash << std::setw(2) << std::setfill('0') << std::hex << (int)slotHashBuffer[i]; } } outStream << dumpLsInfo(&lsInfo) << outHash.str(); ALOGD_IF(ese_debug_enabled, "%s exit\n", __func__); return outStream.str(); } /******************************************************************************* ** ** Function: performLSDownload_thread ** ** Description: Perform LS during hal init ** ** Returns: None ** *******************************************************************************/ void* performLSDownload_thread(__attribute__((unused)) void* data) { ALOGD_IF(ese_debug_enabled, "%s enter ", __func__); /*generated SHA-1 string for secureElementLS This will remain constant as handled in secureElement HAL*/ char sha1[] = "6d583e84f2710e6b0f06beebc1a12a1083591373"; uint8_t hash[20] = {0}; for (int i = 0; i < 40; i = i + 2) { hash[i / 2] = (((datahex(sha1[i]) & 0x0F) << 4) | (datahex(sha1[i + 1]) & 0x0F)); } uint8_t resSW[4] = {0x4e, 0x02, 0x69, 0x87}; std::string sourcePath; std::string sourcePrefix; std::string outPath; int index = 1; LSCSTATUS status = LSCSTATUS_SUCCESS; Lsc_HashInfo_t lsHashInfo; getLSScriptSourcePrefix(sourcePrefix); do { /*Open the script file from specified location and name*/ sourcePath.assign(sourcePrefix); sourcePath += ('0' + index); sourcePath += ls_script_source_suffix; FILE* fIn = fopen(sourcePath.c_str(), "rb"); if (fIn == NULL) { ALOGE("%s Cannot open LS script file %s, Error: %s\n", __func__, sourcePath.c_str(), strerror(errno)); break; } ALOGD_IF(ese_debug_enabled, "%s File opened %s\n", __func__, sourcePath.c_str()); /*Read the script content to a local buffer*/ fseek(fIn, 0, SEEK_END); long lsBufSize = ftell(fIn); if (lsBufSize < 0) { ALOGE("%s Failed to get current value of position indicator\n", __func__); fclose(fIn); status = LSCSTATUS_FAILED; break; } rewind(fIn); if (lsHashInfo.lsRawScriptBuf == nullptr) { lsHashInfo.lsRawScriptBuf = (uint8_t*)phNxpEse_memalloc(lsBufSize + 1); } memset(lsHashInfo.lsRawScriptBuf, 0x00, (lsBufSize + 1)); if (fread(lsHashInfo.lsRawScriptBuf, (size_t)lsBufSize, 1, fIn) != 1) ALOGD_IF(ese_debug_enabled, "%s Failed to read file", __func__); fclose(fIn); LSCSTATUS lsHashStatus = LSCSTATUS_FAILED; /*Get 20bye SHA1 of the script*/ lsHashInfo.lsScriptHash = getHASH(lsHashInfo.lsRawScriptBuf, (size_t)lsBufSize); phNxpEse_free(lsHashInfo.lsRawScriptBuf); lsHashInfo.lsRawScriptBuf = nullptr; if (lsHashInfo.lsScriptHash == nullptr) break; if (lsHashInfo.readBuffHash == nullptr) { lsHashInfo.readBuffHash = (uint8_t*)phNxpEse_memalloc(HASH_DATA_LENGTH); } memset(lsHashInfo.readBuffHash, 0x00, HASH_DATA_LENGTH); /*Read the hash from applet for specified slot*/ lsHashStatus = LSC_ReadLsHash(lsHashInfo.readBuffHash, &lsHashInfo.readHashLen, index); /*Check if previously script is successfully installed. if yes, continue reading next script else try update wit current script*/ if ((lsHashStatus == LSCSTATUS_SUCCESS) && (lsHashInfo.readHashLen == HASH_DATA_LENGTH) && (0 == memcmp(lsHashInfo.lsScriptHash, lsHashInfo.readBuffHash, HASH_DATA_LENGTH - 1)) && (lsHashInfo.readBuffHash[HASH_STATUS_INDEX] == LS_DOWNLOAD_SUCCESS)) { ALOGD_IF(ese_debug_enabled, "%s LS Loader sript is already installed \n", __func__); continue; } /*Create output file to write response data*/ outPath.assign(ls_script_output_prefix); outPath += ('0' + index); outPath += ls_script_output_suffix; FILE* fOut = fopen(outPath.c_str(), "wb+"); if (fOut == NULL) { ALOGE("%s Failed to open file %s\n", __func__, outPath.c_str()); break; } fclose(fOut); /*Uptdates current script*/ status = LSC_Start(sourcePath.c_str(), outPath.c_str(), (uint8_t*)hash, (uint16_t)sizeof(hash), resSW); ALOGD_IF(ese_debug_enabled, "%s script %s perform done, result = %d\n", __func__, sourcePath.c_str(), status); if (status != LSCSTATUS_SUCCESS) { lsHashInfo.lsScriptHash[HASH_STATUS_INDEX] = LS_DOWNLOAD_FAILED; /*If current script updation fails, update the status with hash to the * applet then clean and exit*/ lsHashStatus = LSC_UpdateLsHash(lsHashInfo.lsScriptHash, HASH_DATA_LENGTH, index); if (lsHashStatus != LSCSTATUS_SUCCESS) { ALOGD_IF(ese_debug_enabled, "%s LSC_UpdateLsHash Failed\n", __func__); } ESESTATUS estatus = phNxpEse_deInit(); if (estatus == ESESTATUS_SUCCESS) { estatus = phNxpEse_close(); if (estatus == ESESTATUS_SUCCESS) { ALOGD_IF(ese_debug_enabled, "%s: Ese_close success\n", __func__); } } else { ALOGE("%s: Ese_deInit failed", __func__); } if (mCallback != nullptr) { (mCallback)(false, printLSStatus(LS_DOWNLOAD_FAILED), mCallbackParams); break; } } else { /*If current script execution is succes, update the status along with the * hash to the applet*/ lsHashInfo.lsScriptHash[HASH_STATUS_INDEX] = LS_DOWNLOAD_SUCCESS; lsHashStatus = LSC_UpdateLsHash(lsHashInfo.lsScriptHash, HASH_DATA_LENGTH, index); if (lsHashStatus != LSCSTATUS_SUCCESS) { ALOGD_IF(ese_debug_enabled, "%s LSC_UpdateLsHash Failed\n", __func__); } } } while (++index <= LS_MAX_COUNT); phNxpEse_free(lsHashInfo.readBuffHash); if (status == LSCSTATUS_SUCCESS) { if (mCallback != nullptr) { (mCallback)(true, printLSStatus(LS_DOWNLOAD_SUCCESS), mCallbackParams); } } pthread_exit(NULL); ALOGD_IF(ese_debug_enabled, "%s pthread_exit\n", __func__); return NULL; } /******************************************************************************* ** ** Function: getHASH ** ** Description: generates SHA1 of given buffer ** ** Returns: 20 bytes of SHA1 ** *******************************************************************************/ unsigned char* getHASH(uint8_t* buffer, size_t buffSize) { static uint8_t outHash[HASH_DATA_LENGTH] = {0}; unsigned int md_len = -1; const EVP_MD* md = EVP_get_digestbyname("SHA1"); if (NULL != md) { EVP_MD_CTX mdctx; EVP_MD_CTX_init(&mdctx); EVP_DigestInit_ex(&mdctx, md, NULL); EVP_DigestUpdate(&mdctx, buffer, buffSize); EVP_DigestFinal_ex(&mdctx, outHash, &md_len); EVP_MD_CTX_cleanup(&mdctx); } return outHash; } /******************************************************************************* ** ** Function: datahex ** ** Description: Converts char to uint8_t ** ** Returns: uint8_t variable ** *******************************************************************************/ uint8_t datahex(char c) { uint8_t value = 0; if (c >= '0' && c <= '9') value = (c - '0'); else if (c >= 'A' && c <= 'F') value = (10 + (c - 'A')); else if (c >= 'a' && c <= 'f') value = (10 + (c - 'a')); return value; }