/** * @file A71HLSEWrapper.c * @author NXP Semiconductors * @version 1.0 * @par License * * Copyright 2017,2020 NXP * SPDX-License-Identifier: Apache-2.0 * * @par Description * Host Lib wrapper API implementation over the A71CH host library * * @par HISTORY * */ #include #include "HLSEAPI.h" #include "ax_api.h" #include "a71_debug.h" #include "hcAsn.h" #include "sm_apdu.h" #include #include #include #include #if SSS_HAVE_A71CH || SSS_HAVE_A71CH_SIM /// Key Operations typedef struct { U8 keyVersion; //!< key Version U8 keyEnc[16]; //!< Enc Key U8 keyMac[16]; //!< Mac Key U8 keyDek[16]; //!< Dek Key U8* currentKeyDek; //!< Current Dek Key U16 currentKeyDekLen; //!< Current Dek Key length } HLSE_A71_SM_KEYS_DATA; #define CREDENTIAL_UNLOCKED 0x05 //!< Credential Unlocked #define CREDENTIAL_LOCKED 0x0F //!< Credential locked #define CREDENTIAL_ABSENT 0xA0 //!< Credential Absent #define CREDENTIAL_INITIALIZED 0x50 //!< Credential initialized // GetCredentialnfo returns Credential states values as below (see 6.2.2.2 CredentialInfo) : #define CREDENTIAL_UNLOCKED_ABSENT CREDENTIAL_UNLOCKED | CREDENTIAL_ABSENT //!< Credential unlocked and absent #define CREDENTIAL_UNLOCKED_INITIALIZED CREDENTIAL_UNLOCKED | CREDENTIAL_INITIALIZED //!< Credential unlocked and initialized #define CREDENTIAL_LOCKED_ABSENT CREDENTIAL_LOCKED | CREDENTIAL_ABSENT //!< Credential locked and absent #define CREDENTIAL_LOCKED_INITIALIZED CREDENTIAL_LOCKED | CREDENTIAL_INITIALIZED //!< Credential locked and initialized /// offsets in CredentialinfoTable (CIT) returned from GetCredentialInfo /// as of applet 1.3 (in ROM only) #define CIT_SCP_OFFSET 0 /// 4 key pairs #define CIT_KEYPAIR_OFFSET 1 /// 3 pub keys #define CIT_PUBKEY_OFFSET 5 /// 3 config keys #define CIT_CONFIGKEY_OFFSET 8 /// 8 symmetric keys #define CIT_SYMMETRICKEY_OFFSET 11 /// 2 counters #define CIT_COUNTER_OFFSET 19 /// GP Storage #define CIT_GP_OFFSET 21 //******************************************************************* // Logical objects (e.g. Certificates) in GP Data //******************************************************************* /// Set this to 1 if more than one process acesses the SE and may update the mapping table //#define HLSE_MULTI_PROCESS 1 #define HLSE_GP_DATA_CHUNK 32 //!< GP Data chunk size bytes #define HLSE_FREE_CHUNK 0 //!< Free chunk indication #define HLSE_OCCUPIED_CHUNK 1 //!< Occupied chunk indication #define HLSE_LOCKED_CHUNK 2 //!< Locked Chunk indication #define HLSE_INDIRECT_LENGTH 0x8000 //!< indirect length indication - obtain from TLV /// Alligned number of chunks in which 'size' bytes occupies #define HLSE_ALIGN_SIZE(size) ((size + HLSE_GP_DATA_CHUNK - 1) / HLSE_GP_DATA_CHUNK) // Max chunks which could be allowed in GP Data table #define HLSE_GP_DATA_CHUNKS_NUM HLSE_ALIGN_SIZE(((HLSE_MAX_OBJECTS_IN_TABLE * 6) + 2)) /// To hold GP Size static U16 gGPSize = 0; /// GP Table Data Entry typedef struct { U8 klass; U8 index; U16 length; U16 offset; } HLSE_GP_DATA_TABLE_ENTRY; #define HLSE_GP_DATA_TABLE_ENTRY_SIZE 6 //!< GP Table Entry size in bytes // This is a theoritical max , as it is stored as one byte in the beginning of the Gp Table - up to max 254 entries (0xFE) // elements might be created . value of 0xFF is reserved for a deleted/invalid entry #ifndef HLSE_MAX_OBJECTS_IN_TABLE #define HLSE_MAX_OBJECTS_IN_TABLE 254 #endif /// GP Data Table typedef struct { U8 numOfEntries; //!< num of entries U8 updateCounter; //!< update counter HLSE_GP_DATA_TABLE_ENTRY entries[HLSE_MAX_OBJECTS_IN_TABLE]; //!< entries array } HLSE_GP_DATA_TABLE; /// cached Mapping table static HLSE_GP_DATA_TABLE gMappingTable; /// indication whether cahced mapping table already read static U8 gMappingTableRead = 0; /** * Internal * find out if a requested area in the gp storage is unlocked * * \param [in] dataOffset Offset for the data in the GP Storage. * \param [in] dataLen Amount of data to write * \param [in] map IN: credentials info map * \param [in] mapLen IN: credentials info mapLen * \retval true if memory is not locked */ static U8 MemoryIsUnlocked(U16 dataOffset, U16 dataLen, U8* map, U16 mapLen) { U16 gpdataOffset = dataOffset; int gpStatusOffset; // GP Storage offsets in CredentialinfoTable returned from GetCredentialInfo int gpStatusN; // size of GP storage map in CredentialinfoTable returned from GetCredentialInfo U8 credtialstateLocked = 0x0F; // nibble of lock state in map U8 fLocked = 0; // flag to indicate that a locked segment was found gpStatusN = (A71CH_GP_STORAGE_SIZE_B / A71CH_GP_STORAGE_GRANULARITY); gpStatusOffset = mapLen - gpStatusN; // loop over each segment in required write area to find out weather it is locked while (gpdataOffset < (dataOffset + dataLen)) { fLocked = ((map[(gpdataOffset / A71CH_GP_STORAGE_GRANULARITY) + gpStatusOffset] & 0x0F) == credtialstateLocked) ? 0x01 : 0x00; if (fLocked) { break; } gpdataOffset += A71CH_GP_STORAGE_GRANULARITY; } return (!fLocked); } /** * Get The General Purpose memory size in bytes * * for internal use * * \param[out] gpSize OUT: General Purpose Data size in bytes * * \retval ::HLSE_SW_OK Successfull execution * \retval ::HLSE_ERR_API_ERROR Invalid function arguments */ static HLSE_RET_CODE GetGPDataSize(U16* gpSize) { HLSE_RET_CODE lReturn = HLSE_SW_OK; // Get the Module's handle HLSE_OBJECT_HANDLE modHandle = 0; //U16 modHandleNum = 1; if (gGPSize != 0) { *gpSize = gGPSize; return HLSE_SW_OK; } modHandle = HLSE_CREATE_HANDLE(1, HLSE_MODULE, 0); { HLSE_ATTRIBUTE attr; attr.type = HLSE_ATTR_MODULE_TOTAL_GP_SIZE; attr.value = gpSize; attr.valueLen = sizeof(U16); lReturn = HLSE_GetObjectAttribute(modHandle, &attr); } gGPSize = *gpSize; return lReturn; } /** * Sort GP mapping Table * * Note : Since sorted by offset any deleted/invalid entries (value 0xFFFF) are moved * to the end of the table when function is finished * * \param[in,out] table IN: pointer to gp mapping table, OUT: sorted table */ static void SortTable(HLSE_GP_DATA_TABLE* table) { U8 i,j; if (table->numOfEntries >= 1) { for (i = 0; i < (HLSE_MAX_OBJECTS_IN_TABLE - 1); ++i) { for (j = i + 1; j < HLSE_MAX_OBJECTS_IN_TABLE; ++j) { if (table->entries[i].offset > table->entries[j].offset) { // exchange entry positions HLSE_GP_DATA_TABLE_ENTRY temp; memcpy(&temp, &table->entries[i], sizeof(temp)); memcpy(&table->entries[i], &table->entries[j], sizeof(temp)); memcpy(&table->entries[j], &temp, sizeof(temp)); } } } } } /** * Read the General Purpose mapping Data Table * * for internal use * * \param [out] table OUT: General Purpose Data table read * \param [in] forceReadFromGPData 1 to force read the table from GP memory instead of from cache, 0 read it from cached varaiable * * \retval ::HLSE_SW_OK Successfull execution */ static HLSE_RET_CODE ReadGPDataTable(HLSE_GP_DATA_TABLE* table, U8 forceReadFromGPData) { U16 gpSize; HLSE_RET_CODE lReturn = HLSE_SW_OK; // Storage to be able to read from GP memoy up to theortical max number of entries in the GP table // this storage will mirror the end of GP Memory containing the GP table U8 dataRead[HLSE_GP_DATA_CHUNK * HLSE_GP_DATA_CHUNKS_NUM]; U8 entryNum; U16 dataReadByteSize = sizeof(dataRead); int nObj; U8 bValidEntry; U8 nMaxObj; U8 tmpClass, tmpIndex; #ifndef HLSE_MULTI_PROCESS if (forceReadFromGPData == 0) { if (gMappingTableRead) { memcpy(table, &gMappingTable, sizeof(gMappingTable)); return HLSE_SW_OK; } } #endif lReturn = GetGPDataSize(&gpSize); if (lReturn != HLSE_SW_OK) return lReturn; memset(table, 0xFF, sizeof(HLSE_GP_DATA_TABLE)); table->numOfEntries = 0; table->updateCounter = 0; // Read last chunk of Gp table to fetch the actual number of entries in it , then read additional chunks of the GP table if required memset(dataRead, 0xFF, sizeof(dataRead)); lReturn = A71_GetGpData(gpSize - HLSE_GP_DATA_CHUNK, // start address of last chunk in GP memory to read from (dataRead + dataReadByteSize - HLSE_GP_DATA_CHUNK), // destination HLSE_GP_DATA_CHUNK); // num of bytes to read (= 1 chunk) if (lReturn != HLSE_SW_OK) { return lReturn; } table->numOfEntries = dataRead[dataReadByteSize - 1]; if (table->numOfEntries == 0xFF) { // the table doesnt exist or invalid - use a dummy one memcpy(&gMappingTable, table, sizeof(gMappingTable)); gMappingTableRead = 1; return HLSE_SW_OK; } table->updateCounter = dataRead[dataReadByteSize - 2]; // if num of entries is more than 5 then we should read additional chunks into the correct address of 'dataRead', notice no need to read last chunk which was already read if (table->numOfEntries > 5) { U8 nCurGpTableChunks = HLSE_ALIGN_SIZE(((table->numOfEntries * 6) + 2)); // read the additional table chunks from GP memory, up till the last chunk which we already read lReturn = A71_GetGpData(gpSize - (HLSE_GP_DATA_CHUNK * nCurGpTableChunks), // start address to read from (dataRead + dataReadByteSize - (HLSE_GP_DATA_CHUNK * nCurGpTableChunks)), // destination (HLSE_GP_DATA_CHUNK * (nCurGpTableChunks-1))); // num of bytes to read if (lReturn != HLSE_SW_OK) { return lReturn; } } // Read only Valid entries up to numOfEntries starting from end of GP storage ( high address to low address ) // note : it is assumed that the table is consecutive with valid entries - since whenever an object is deleted // in DeleteGPDataTableEntry() - the table is sorted and compacted so any invalid entries are moved to the end nObj = 1; // first object to check if valid in GP Table for (entryNum = 0; entryNum < table->numOfEntries; ++entryNum) { // Notes: // X + 1 is the address of the last byte of the GP Storage. // N is the object number from 1 to N // read from object 1 to numOfEntries /* Address Value ------- ---------------------- X-N*6+0 N'th Object Class - 1 byte X-N*6+1 N'th Object Index - 1 byte X-N*6+2 N'th Object Length MSB - 1 byte X-N*6+3 N'th Object Length LSB - 1 byte X-N*6+4 N'th Object Offset MSB - 1 byte X-N*6+5 N'th Object Offset LSB - 1 byte */ // Note : Since the table is kept compacted with invalid entries moved to its end whenever an entry is deleted - // the following code to skip invalid entries is here just to be on the safe side in case provisioning was done incorrectly somehow, // with the table created initially not with consecutive valid entries // { bValidEntry = 0; // max objects that could be held in the gp data table nMaxObj = HLSE_MAX_OBJECTS_IN_TABLE; while (!bValidEntry && nObj <= nMaxObj) { // check if entry is valid tmpClass = dataRead[dataReadByteSize - 2 - nObj * 6 + 0]; tmpIndex = dataRead[dataReadByteSize - 2 - nObj * 6 + 1]; // skip to next valid entry if (tmpClass != 0xFF && tmpIndex != 0xFF) { // this is a valid entry bValidEntry = 1; break; } nObj++; // skip to next object } if (!bValidEntry) { // error - no more valid entries in table , although num of entries in gp table says there are more ! break; } // } // fill this valid entry in our table table->entries[entryNum].klass = dataRead[dataReadByteSize - 2 - nObj * 6 + 0]; table->entries[entryNum].index = dataRead[dataReadByteSize - 2 - nObj * 6 + 1]; table->entries[entryNum].length = dataRead[dataReadByteSize - 2 - nObj * 6 + 2] * 256 | dataRead[dataReadByteSize - 2 - nObj * 6 + 3]; table->entries[entryNum].offset = dataRead[dataReadByteSize - 2 - nObj * 6 + 4] * 256 | dataRead[dataReadByteSize - 2 - nObj * 6 + 5]; nObj++; // skip to next object } // sort the entries in ascending order of the offset SortTable(table); memcpy(&gMappingTable, table, sizeof(gMappingTable)); gMappingTableRead = 1; return lReturn; } /** * Write the General Purpose mapping Data Table to Gp Memory and to cached variable * * for internal use * * \param [in] table IN: General Purpose Data table to write * * \retval ::HLSE_SW_OK Successfull execution */ static HLSE_RET_CODE WriteGPDataTable(HLSE_GP_DATA_TABLE* table) { U16 gpSize; HLSE_RET_CODE lReturn = HLSE_SW_OK; U8 dataToBeWritten[HLSE_GP_DATA_CHUNK * HLSE_GP_DATA_CHUNKS_NUM]; U8 entryNum; U16 dataToBeWrittenSize = sizeof(dataToBeWritten); U8 nCurGpTableChunks; lReturn = GetGPDataSize(&gpSize); if (lReturn != HLSE_SW_OK) { return lReturn; } memset(dataToBeWritten, 0xFF, dataToBeWrittenSize); dataToBeWritten[dataToBeWrittenSize - 1] = table->numOfEntries; #ifndef HLSE_DISABLE_UPDATE_COUNTER table->updateCounter++; #endif dataToBeWritten[dataToBeWrittenSize - 2] = table->updateCounter; // fill in the table with objects up to num of entries , in gp storage from high memory address to low memory address for (entryNum = 0; entryNum < table->numOfEntries; ++entryNum) { dataToBeWritten[dataToBeWrittenSize - 2 - (entryNum + 1) * 6 + 0] = table->entries[entryNum].klass; dataToBeWritten[dataToBeWrittenSize - 2 - (entryNum + 1) * 6 + 1] = table->entries[entryNum].index; dataToBeWritten[dataToBeWrittenSize - 2 - (entryNum + 1) * 6 + 2] = (table->entries[entryNum].length >> 8) & 0xFF; dataToBeWritten[dataToBeWrittenSize - 2 - (entryNum + 1) * 6 + 3] = table->entries[entryNum].length & 0xFF; dataToBeWritten[dataToBeWrittenSize - 2 - (entryNum + 1) * 6 + 4] = (table->entries[entryNum].offset >> 8) & 0xFF; dataToBeWritten[dataToBeWrittenSize - 2 - (entryNum + 1) * 6 + 5] = table->entries[entryNum].offset & 0xFF; } // Write the number of Gp Table chunks required according to 'numOfEntries' nCurGpTableChunks = HLSE_ALIGN_SIZE(((table->numOfEntries * 6) + 2)); lReturn = A71_SetGpData(gpSize - (nCurGpTableChunks * HLSE_GP_DATA_CHUNK), // offset in GP memory to write dataToBeWritten + dataToBeWrittenSize - (nCurGpTableChunks * HLSE_GP_DATA_CHUNK), // data to write (nCurGpTableChunks * HLSE_GP_DATA_CHUNK)); // data len if (lReturn != HLSE_SW_OK) { if (lReturn == SW_COMMAND_NOT_ALLOWED) { // propagates a clear meaning to the user, usefull for Usecase when GP table is locked but still direct partial update of object might be allowed lReturn = HLSE_OBJ_GP_TABLE_LOCKED; } return lReturn; } memcpy(&gMappingTable, table, sizeof(gMappingTable)); gMappingTableRead = 1; return lReturn; } /** * Update an entry in the General Purpose mapping Data Table in Gp Memory and to cached variable * * for internal use * * \param [in] entry IN: General Purpose Data table entry to update * * \retval ::HLSE_SW_OK Successfull execution */ static HLSE_RET_CODE UpdateGPDataTableEntry(HLSE_GP_DATA_TABLE_ENTRY* entry) { HLSE_GP_DATA_TABLE table; HLSE_RET_CODE lReturn = HLSE_SW_OK; U8 entryNum; lReturn = ReadGPDataTable(&table, 0); if (lReturn != HLSE_SW_OK) return lReturn; // find the entry according to the index for (entryNum = 0; entryNum < table.numOfEntries; ++entryNum) { if (table.entries[entryNum].index == entry->index && table.entries[entryNum].klass == entry->klass) { memcpy(&table.entries[entryNum], entry, sizeof(HLSE_GP_DATA_TABLE_ENTRY)); break; } } lReturn = WriteGPDataTable(&table); return lReturn; } /** * Delete an entry in the General Purpose mapping Data Table in Gp Memory and to cached variable * * for internal use * An entry is marked as deleted/invalid by setting all its memory bytes to 0xFF, the table is then sorted by offset, resulting * all deleted/invalid entries to be moved to the end of the table * * \param [in] index index idntifier of object entry to delete * \param [in] objClass class identifier of object entry to delete * * \retval ::HLSE_SW_OK Successfull execution */ static HLSE_RET_CODE DeleteGPDataTableEntry(U8 index, U8 objClass) { HLSE_GP_DATA_TABLE table; HLSE_RET_CODE lReturn = HLSE_SW_OK; U8 entryNum; lReturn = ReadGPDataTable(&table, 0); if (lReturn != HLSE_SW_OK) return lReturn; // find the entry according to the index for (entryNum = 0; entryNum < table.numOfEntries; ++entryNum) { if (table.entries[entryNum].index == index && table.entries[entryNum].klass == objClass) { HLSE_GP_DATA_TABLE_ENTRY entry; memset(&entry, 0xFF, sizeof(entry)); memcpy(&table.entries[entryNum], &entry, sizeof(HLSE_GP_DATA_TABLE_ENTRY)); table.numOfEntries--; break; } } // sort the entries in ascending order of the offset, note : any invalid/deleted entries will be moved to the end SortTable(&table); lReturn = WriteGPDataTable(&table); return lReturn; } /// Get File Size base on TLV in data - for internal use static U16 GetFileSize(U8* data) { U16 dataLen = 6; // calculate the Tag's length U8 TagLen = 1; if ((data[0] & 0x1F) == 0x1F) { TagLen = 2; if (data[1] & 0x80) { TagLen = 3; } } if (data[TagLen] & 0x80){ if (data[TagLen] == 0x81) dataLen = data[TagLen + 1] + 2 + TagLen; else { dataLen = data[TagLen + 1] * 256 + data[TagLen + 2] + 3 + TagLen; } } else dataLen = data[TagLen] + 1 + TagLen; return dataLen; } /** * Get an Object's Offset and Length * * for internal use * * \param [in] index index idntifier of object entry * \param [in] objClass class identifier of object entry * \param [out] offset object's offset in bytes * \param [out] length object's length in bytes - in case of indirect length in the table, read it from actual object * \param [out] availLength - total available length * * \retval ::HLSE_SW_OK Successfull execution */ static HLSE_RET_CODE GetObjectOffsetAndLength(U8 index, U8 objClass, U16* offset, U16* length, U16* availLength) { HLSE_GP_DATA_TABLE table; HLSE_RET_CODE lReturn = HLSE_SW_OK; U8 entryNum; U8 nCurGpTableChunks; lReturn = ReadGPDataTable(&table, 0); if (lReturn != HLSE_SW_OK) return lReturn; // find the entry according to the index and klass for (entryNum = 0; entryNum < table.numOfEntries; ++entryNum) { if (table.entries[entryNum].index == index && table.entries[entryNum].klass == objClass) { *offset = table.entries[entryNum].offset; // length is known if (!(table.entries[entryNum].length & HLSE_INDIRECT_LENGTH)) { *length = table.entries[entryNum].length; } // INDIRECT length else if (table.entries[entryNum].length == HLSE_INDIRECT_LENGTH) { // get the length from the TLV data U8 header[6]; lReturn = A71_GetGpData(table.entries[entryNum].offset, header, 6); if (lReturn != HLSE_SW_OK) return lReturn; *length = GetFileSize(header); gMappingTable.entries[entryNum].length = HLSE_INDIRECT_LENGTH | *length; } else { *length = table.entries[entryNum].length & 0x7FFF; } // obtain total available length // 2 cases: // 1) in case of Certificate type - get the length up to the next object in the table // 2) in case of data object - get the length of the data object if (table.entries[entryNum].klass == HLSE_GET_LOGICAL_OBJECT_CLASS(HLSE_CERTIFICATE)) { // Get the length up to the next object in the table if (entryNum < (table.numOfEntries - 1)) { *availLength = table.entries[entryNum + 1].offset - table.entries[entryNum].offset; } else // this is the last entry so we need to find the max length till beginning of GP table data { nCurGpTableChunks = HLSE_ALIGN_SIZE(((table.numOfEntries * 6) + 2)); *availLength = gGPSize - (nCurGpTableChunks * HLSE_GP_DATA_CHUNK) - table.entries[entryNum].offset; } } else // assume a data object { // length for Data object is always well known *availLength = table.entries[entryNum].length; } break; } } return lReturn; } /// for internal use - Get Object data for object identified by 'index' and 'objClass' static HLSE_RET_CODE GetObjectData(U8 index, U8 objClass, U8* objData, U16* objDataLen) { HLSE_RET_CODE lReturn = HLSE_SW_OK; U16 length, offset; U16 availLength; // total available length lReturn = GetObjectOffsetAndLength(index, objClass, &offset, &length, &availLength); if (lReturn != HLSE_SW_OK) return lReturn; if (objData == NULL) { *objDataLen = length; return HLSE_SW_OK; } else if (*objDataLen < length) { *objDataLen = length; return HLSE_ERR_BUF_TOO_SMALL; } // if this is a certificate , get its actual length from the TLV header and return only the certificate's data, without trailing padding if any if (objClass == HLSE_GET_LOGICAL_OBJECT_CLASS(HLSE_CERTIFICATE)) { // get the length from the TLV data U8 header[6]; U16 certLength = 0; lReturn = A71_GetGpData(offset, header, 6); if (lReturn != HLSE_SW_OK) { return lReturn; } certLength = GetFileSize(header); if (certLength < length) { length = certLength; // actual cert length without trailing padding } } *objDataLen = length; return A71_GetGpData(offset, objData, length); } /// for internal use - Get Direct Access Object data for object identified by 'index' and 'objClass' static HLSE_RET_CODE DirectAccessGetObjectData(U8 index, U8 objClass, U16 internalOffset, U16 bytes, U8* objData, U16* objDataLen) { HLSE_RET_CODE lReturn = HLSE_SW_OK; U16 length, offset; U16 availLength; // total available length lReturn = GetObjectOffsetAndLength(index, objClass, &offset, &length, &availLength); if (lReturn != HLSE_SW_OK) return lReturn; // check that we can read as many bytes as requested if (internalOffset > length || (internalOffset + bytes) > length) return HLSE_ERR_MEMORY; if (objData == NULL) { *objDataLen = bytes; return HLSE_SW_OK; } else if (*objDataLen < bytes) { *objDataLen = bytes; return HLSE_ERR_BUF_TOO_SMALL; } *objDataLen = bytes; return A71_GetGpData(offset + internalOffset, objData, bytes); } /// for internal use - Update Object data for object identified by 'index' and 'objClass' static HLSE_RET_CODE UpdateObjectData(U8 index, U8 objClass, U8* newData, U16 newDataLen) { HLSE_GP_DATA_TABLE_ENTRY entry; HLSE_RET_CODE lReturn = HLSE_SW_OK; U16 length, offset; U16 availLength; // total available length lReturn = GetObjectOffsetAndLength(index, objClass, &offset, &length, &availLength); if (lReturn != HLSE_SW_OK) return lReturn; // update is only permitted if enough memory is available to grow if (HLSE_ALIGN_SIZE(newDataLen) > HLSE_ALIGN_SIZE(availLength)) { return HLSE_ERR_MEMORY; } entry.index = index; entry.klass = objClass; entry.offset = offset; // leave the max length used so far in the table entry entry.length = (length > newDataLen ? length : newDataLen); lReturn = A71_SetGpDataWithLockCheck(offset, newData, newDataLen); if (lReturn != HLSE_SW_OK) return lReturn; return UpdateGPDataTableEntry(&entry); } /// for internal use - Update Object data for object identified by 'index' and 'objClass' static HLSE_RET_CODE DirectAccessUpdateObjectData(U8 index, U8 objClass, U16 internalOffset, U8* newData, U16 newDataLen) { HLSE_GP_DATA_TABLE_ENTRY entry; HLSE_RET_CODE lReturn = HLSE_SW_OK; U16 length, offset; U16 availLength; // total available length lReturn = GetObjectOffsetAndLength(index, objClass, &offset, &length, &availLength); if (lReturn != HLSE_SW_OK) return lReturn; // update is only permitted within the same chunk number - if larger, delete the object and re-create it if (HLSE_ALIGN_SIZE(internalOffset + newDataLen) > HLSE_ALIGN_SIZE(length)) return HLSE_ERR_MEMORY; entry.index = index; entry.klass = objClass; entry.offset = offset; entry.length = (internalOffset + newDataLen) > length ? (internalOffset + newDataLen) : length; lReturn = A71_SetGpData(offset + internalOffset, newData, newDataLen); if (lReturn != HLSE_SW_OK) return lReturn; #ifdef HLSE_DISABLE_UPDATE_COUNTER if ((internalOffset + newDataLen) > length) { // Direct access has increased the size of the data object, the GPTable must be updated lReturn = UpdateGPDataTableEntry(&entry); } else { lReturn = HLSE_SW_OK; } #else // Update table to reflect // - new value of Update Counter // - possibly a new value for object length lReturn = UpdateGPDataTableEntry(&entry); #endif return lReturn; } /** * for internal use - Get the Offset For a New Object and add its entry to GP table * NOTE: allocation is done in chunks - one chunk can not be shared between objects!!! */ static HLSE_RET_CODE GetOffsetForNewObjectAndAddNewEntry(U16 newObjectSize, HLSE_GP_DATA_TABLE_ENTRY* entry) { HLSE_RET_CODE lReturn = HLSE_SW_OK; HLSE_GP_DATA_TABLE table; U16 gpSize; U16 newObjectSizeInChunks; U8 found = 0; U8 entryNum; U8 bEntryAlreadyExists = 0; // required for retrieving cerdtials map to know if a segment is locked U16 rv = 0; U8 map[A71CH_MAP_SIZE_MAX] = {0}; U16 mapLen = sizeof(map); lReturn = ReadGPDataTable(&table, 0); if (lReturn != HLSE_SW_OK) return lReturn; // Check if data object already exists - if so return an error for (entryNum = 0; entryNum < table.numOfEntries; ++entryNum) { if (table.entries[entryNum].index == entry->index && table.entries[entryNum].klass == entry->klass) { bEntryAlreadyExists = 1; break; } } if (bEntryAlreadyExists) { return HLSE_OBJ_ALREADY_EXISTS; // entry already exists } // resolve INDIRECT lengths for (entryNum = 0; entryNum < table.numOfEntries; ++entryNum) { if (table.entries[entryNum].index != 0xFF) { // INDIRECT length and no real length found yet if (table.entries[entryNum].length == HLSE_INDIRECT_LENGTH) { // get the length from the TLV data U8 header[6]; U16 actualLength = 0; lReturn = A71_GetGpData(table.entries[entryNum].offset, header, 6); if (lReturn != HLSE_SW_OK) return lReturn; actualLength = GetFileSize(header); table.entries[entryNum].length = HLSE_INDIRECT_LENGTH | actualLength; } } } lReturn = GetGPDataSize(&gpSize); if (lReturn != HLSE_SW_OK) return lReturn; // obtain the credentials info map, to be able to check for locked segments rv = A71_GetCredentialInfo(map, &mapLen); if (rv != SW_OK) { return HLSE_ERR_API_ERROR; } // now find a unoccupied (and unlocked) large enough offset for the new object, assuming the table is sorted in ascending order of offsets newObjectSizeInChunks = HLSE_ALIGN_SIZE(newObjectSize); entry->offset = 0; // invalid entries will have length = 0xFFFF so they will be at the end // check if it possible to insert the object at the begining of the GP Storage if (table.numOfEntries == 0 || (table.entries[0].offset / HLSE_GP_DATA_CHUNK) >= newObjectSizeInChunks) { if (MemoryIsUnlocked(0, newObjectSize, map, mapLen)) { entry->offset = 0; found = 1; } } else { U8 leftEntry, rightEntry; for (leftEntry = 0; leftEntry < table.numOfEntries && !found; ++leftEntry) { if (table.entries[leftEntry].index != 0xFF && table.entries[leftEntry].klass != 0xFF) { // leftEntry is a valid entry rightEntry = leftEntry + 1; if (rightEntry < table.numOfEntries && table.entries[rightEntry].index != 0xFF && table.entries[rightEntry].klass != 0xFF) { // rightEntry is a valid entry U16 leftEnd = HLSE_ALIGN_SIZE(table.entries[leftEntry].offset + (table.entries[leftEntry].length & 0x7FFF)); // Check the validiy of candidate chunks from leftEnd to beginning of 'rightEntry' object while (!found && (((table.entries[rightEntry].offset / HLSE_GP_DATA_CHUNK) - leftEnd) >= newObjectSizeInChunks)) { // Verify memory area is unlocked if (MemoryIsUnlocked(leftEnd * HLSE_GP_DATA_CHUNK, newObjectSize, map, mapLen)) { entry->offset = leftEnd; found = 1; } else { // skip to next chunk to begin check leftEnd++; } } } } } if (!found) { // check if there is space in the end // find the last valid entry S8 lastValidEntry = table.numOfEntries - 1; U8 nCurGpTableChunks; for (; lastValidEntry >= 0; --lastValidEntry) { if (table.entries[lastValidEntry].index != 0xFF) break; } // no valid entries - check that the gpData is big enough (excluding last chunk(s) of gp table) nCurGpTableChunks = HLSE_ALIGN_SIZE(((table.numOfEntries * 6) + 2)); if (lastValidEntry == 0 && table.entries[lastValidEntry].index == 0xFF && (HLSE_ALIGN_SIZE(gpSize) - nCurGpTableChunks) >= newObjectSizeInChunks) { if (MemoryIsUnlocked(0, newObjectSize, map, mapLen)) { entry->offset = 0; found = 1; } } else { // check for valid free memory area after the last object U16 prevObjectEnd = HLSE_ALIGN_SIZE(table.entries[lastValidEntry].offset + (table.entries[lastValidEntry].length & 0x7FFF)); while (!found && ((HLSE_ALIGN_SIZE(gpSize) - nCurGpTableChunks - prevObjectEnd) >= newObjectSizeInChunks)) { if (MemoryIsUnlocked(prevObjectEnd * HLSE_GP_DATA_CHUNK, newObjectSize, map, mapLen)) { entry->offset = prevObjectEnd; found = 1; } else { // skip to next chunk to begin check prevObjectEnd++; } } } } } if (found) { entry->offset *= HLSE_GP_DATA_CHUNK; } else { entry->offset = 0; return HLSE_ERR_MEMORY; } // check if there is an empty entry found = 0; for (entryNum = 0; entryNum < table.numOfEntries; ++entryNum) { if (table.entries[entryNum].index == 0xFF) { memcpy(&table.entries[entryNum], entry, sizeof(HLSE_GP_DATA_TABLE_ENTRY)); found = 1; break; } } if (!found) { table.numOfEntries++; if (table.numOfEntries > HLSE_MAX_OBJECTS_IN_TABLE) { return HLSE_ERR_IDENT_IDX_RANGE; } memcpy(&table.entries[table.numOfEntries - 1], entry, sizeof(HLSE_GP_DATA_TABLE_ENTRY)); } // sort the entries in ascending order of the offset SortTable(&table); lReturn = WriteGPDataTable(&table); return lReturn; } //******************************************************************* // Object Operations - defined in HLSEObjects.h //******************************************************************* HLSE_RET_CODE HLSE_EnumerateObjects(HLSE_OBJECT_TYPE objectType, HLSE_OBJECT_HANDLE* objectHandles, U16* objectHandlesLen) { HLSE_GP_DATA_TABLE gpDataTable = { 0 }; U8 credentials[1000] = {0}; U16 credentialsSize = sizeof(credentials); U16 objCount = 0; // the Module always exists U8 fReadOnly = 0; U16 lReturn; #ifndef HLSE_IGNORE_PARAM_CHECK //objectHandles is NULL, then all that the function does is return (in \p *objectHandlesLen) a number of HLSE_OBJECT_HANDLE which would suffice // to hold the returned list. HLSE_SW_OK is returned by the function. //If objectHandles is not NULL, then objectHandlesLen must contain the number of handles in the buffer objectHandles if (objectHandles != NULL && objectHandlesLen == NULL) { return HLSE_ERR_API_ERROR; } if (objectHandlesLen == NULL) { return HLSE_ERR_API_ERROR; } #endif lReturn = A71_GetCredentialInfo(credentials, &credentialsSize); if (lReturn != HLSE_SW_OK) { return lReturn; } if (objectType == HLSE_MODULE) { objCount++; } else if (objectType != HLSE_CERTIFICATE && objectType != HLSE_DATA) { U16 i; if (objectType == HLSE_MODULE || objectType == HLSE_ANY_TYPE) { objCount++; } for (i = 1; i < credentialsSize; ++i) { if (credentials[i] & CREDENTIAL_INITIALIZED) { if (i >= CIT_KEYPAIR_OFFSET && i <= (CIT_PUBKEY_OFFSET -1) && (objectType == HLSE_KEY_PAIR || objectType == HLSE_ANY_TYPE)) objCount++; else if (i >= CIT_PUBKEY_OFFSET && i <= (CIT_CONFIGKEY_OFFSET-1) && (objectType == HLSE_PUBLIC_KEY || objectType == HLSE_ANY_TYPE)) objCount++; else if (i >= CIT_CONFIGKEY_OFFSET && i <= (CIT_SYMMETRICKEY_OFFSET-1) && (objectType == HLSE_CONFIG_KEY || objectType == HLSE_ANY_TYPE)) objCount++; else if (i >= CIT_SYMMETRICKEY_OFFSET && i <= (CIT_COUNTER_OFFSET-1) && (objectType == HLSE_SYMMETRIC_KEY || objectType == HLSE_ANY_TYPE)) objCount++; else if (i >= CIT_COUNTER_OFFSET && i <= (CIT_GP_OFFSET-1) && (objectType == HLSE_COUNTER || objectType == HLSE_ANY_TYPE)) objCount++; } } } // read Certificate/Data objects if (objectType == HLSE_CERTIFICATE || objectType == HLSE_DATA || objectType == HLSE_ANY_TYPE) { lReturn = ReadGPDataTable(&gpDataTable, 0); if (lReturn == HLSE_SW_OK) { U8 entryNum; for (entryNum = 0; entryNum < gpDataTable.numOfEntries; ++entryNum) { if (gpDataTable.entries[entryNum].index != 0xFF) { if (objectType == HLSE_GET_LOGICAL_OBJECT_TYPE(gpDataTable.entries[entryNum].klass) || objectType == HLSE_ANY_TYPE) { objCount++; } } } } } if (objectHandles == NULL) { *objectHandlesLen = objCount; return HLSE_SW_OK; } if (objectHandles != NULL && objectHandlesLen != NULL && *objectHandlesLen < objCount) { *objectHandlesLen = objCount; return HLSE_ERR_BUF_TOO_SMALL; } *objectHandlesLen = 0; if (objectType == HLSE_MODULE || objectType == HLSE_ANY_TYPE) { objectHandles[(*objectHandlesLen)++] = HLSE_CREATE_HANDLE(1, HLSE_MODULE, 0); } if (objectType != HLSE_MODULE && objectType != HLSE_CERTIFICATE && objectType != HLSE_DATA) { U16 i; for (i = 1; i < credentialsSize; ++i) { if (credentials[i] & CREDENTIAL_INITIALIZED) { if (i >= CIT_KEYPAIR_OFFSET && i <= (CIT_PUBKEY_OFFSET - 1) && (objectType == HLSE_KEY_PAIR || objectType == HLSE_ANY_TYPE)) objectHandles[(*objectHandlesLen)++] = HLSE_CREATE_HANDLE(credentials[i] & CREDENTIAL_LOCKED, HLSE_KEY_PAIR, (i - CIT_KEYPAIR_OFFSET)); else if (i >= CIT_PUBKEY_OFFSET && i <= (CIT_CONFIGKEY_OFFSET - 1) && (objectType == HLSE_PUBLIC_KEY || objectType == HLSE_ANY_TYPE)) objectHandles[(*objectHandlesLen)++] = HLSE_CREATE_HANDLE(credentials[i] & CREDENTIAL_LOCKED, HLSE_PUBLIC_KEY, (i - CIT_PUBKEY_OFFSET)); else if (i >= CIT_CONFIGKEY_OFFSET && i <= (CIT_SYMMETRICKEY_OFFSET - 1) && (objectType == HLSE_CONFIG_KEY || objectType == HLSE_ANY_TYPE)) objectHandles[(*objectHandlesLen)++] = HLSE_CREATE_HANDLE(credentials[i] & CREDENTIAL_LOCKED, HLSE_CONFIG_KEY, (i - CIT_CONFIGKEY_OFFSET)); else if (i >= CIT_SYMMETRICKEY_OFFSET && i <= (CIT_COUNTER_OFFSET - 1) && (objectType == HLSE_SYMMETRIC_KEY || objectType == HLSE_ANY_TYPE)) objectHandles[(*objectHandlesLen)++] = HLSE_CREATE_HANDLE(credentials[i] & CREDENTIAL_LOCKED, HLSE_SYMMETRIC_KEY, (i - CIT_SYMMETRICKEY_OFFSET)); else if (i >= CIT_COUNTER_OFFSET && i <= (CIT_GP_OFFSET - 1) && (objectType == HLSE_COUNTER || objectType == HLSE_ANY_TYPE)) objectHandles[(*objectHandlesLen)++] = HLSE_CREATE_HANDLE(credentials[i] & CREDENTIAL_LOCKED, HLSE_COUNTER, (i - CIT_COUNTER_OFFSET)); } } } // add Certificate objects if (objectType == HLSE_CERTIFICATE || objectType == HLSE_DATA || objectType == HLSE_ANY_TYPE) { U8 entryNum; for (entryNum = 0; entryNum < gpDataTable.numOfEntries; ++entryNum) { if (gpDataTable.entries[entryNum].index != 0xFF) { if (objectType == HLSE_GET_LOGICAL_OBJECT_TYPE(gpDataTable.entries[entryNum].klass) || objectType == HLSE_ANY_TYPE) { fReadOnly = ((credentials[(gpDataTable.entries[entryNum].offset / HLSE_GP_DATA_CHUNK) + CIT_GP_OFFSET] & 0x0F) == CREDENTIAL_LOCKED ) ? 0x01 : 0x00; objectHandles[(*objectHandlesLen)++] = HLSE_CREATE_HANDLE(fReadOnly, HLSE_GET_LOGICAL_OBJECT_TYPE(gpDataTable.entries[entryNum].klass), gpDataTable.entries[entryNum].index); } } } } return HLSE_SW_OK; } HLSE_RET_CODE HLSE_SetObjectAttribute(HLSE_OBJECT_HANDLE hObject, HLSE_ATTRIBUTE* attribute) { #ifndef HLSE_IGNORE_PARAM_CHECK if ((attribute == NULL) || (hObject == HLSE_ANY_TYPE)) { return HLSE_ERR_API_ERROR; } #endif //*************************** // HLSE_KEY_PAIR //*************************** if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_KEY_PAIR) { if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { if (attribute->value == NULL) // generate return A71_GenerateEccKeyPair(HLSE_GET_OBJECT_INDEX(hObject)); if (attribute->valueLen != 97) return HLSE_ERR_API_ERROR; return A71_SetEccKeyPair(HLSE_GET_OBJECT_INDEX(hObject), &((U8*)(attribute->value))[32], 65, (U8*)(attribute->value), 32); } else if (attribute->type == HLSE_ATTR_WRAPPED_OBJECT_VALUE) { if (attribute->valueLen != 105) return HLSE_ERR_API_ERROR; return A71_SetEccKeyPair(HLSE_GET_OBJECT_INDEX(hObject), &((U8*)(attribute->value))[40], 65, (U8*)(attribute->value), 40); } else return HLSE_ERR_API_ERROR; } //*************************** // HLSE_PUBLIC_KEY //*************************** else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_PUBLIC_KEY) { if (attribute->value == NULL || attribute->valueLen == 0) return HLSE_ERR_API_ERROR; if (attribute->type == HLSE_ATTR_OBJECT_VALUE) return A71_SetEccPublicKey(HLSE_GET_OBJECT_INDEX(hObject), (U8*)(attribute->value), attribute->valueLen); else return HLSE_ERR_API_ERROR; } //*************************** // HLSE_SYMMETRIC_KEY //*************************** else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_SYMMETRIC_KEY) { if (attribute->value == NULL) return HLSE_ERR_API_ERROR; if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { if (attribute->valueLen == 0) return HLSE_ERR_API_ERROR; return A71_SetSymKey(HLSE_GET_OBJECT_INDEX(hObject), (U8*)(attribute->value), attribute->valueLen); } else if (attribute->type == HLSE_ATTR_WRAPPED_OBJECT_VALUE) { if (attribute->valueLen == 0) return HLSE_ERR_API_ERROR; return A71_SetRfc3394WrappedAesKey(HLSE_GET_OBJECT_INDEX(hObject), (U8*)(attribute->value), attribute->valueLen); } else return HLSE_ERR_API_ERROR; } //*************************** // HLSE_CONFIG_KEY //*************************** else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_CONFIG_KEY) { if (attribute->value == NULL) return HLSE_ERR_API_ERROR; if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { if (attribute->valueLen == 0) return HLSE_ERR_API_ERROR; return A71_SetConfigKey(HLSE_GET_OBJECT_INDEX(hObject), (U8*)(attribute->value), attribute->valueLen); } else if (attribute->type == HLSE_ATTR_WRAPPED_OBJECT_VALUE) { if (attribute->valueLen == 0) return HLSE_ERR_API_ERROR; return A71_SetRfc3394WrappedConfigKey(HLSE_GET_OBJECT_INDEX(hObject), (U8*)(attribute->value), attribute->valueLen); } else return HLSE_ERR_API_ERROR; } //*************************** // HLSE_COUNTER //*************************** else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_COUNTER) { if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { if (attribute->valueLen == 0 && attribute->value == NULL) return A71_IncrementCounter(HLSE_GET_OBJECT_INDEX(hObject)); else { unsigned long val ; if (attribute->valueLen == 0 || attribute->value == NULL) return HLSE_ERR_API_ERROR; val = *(U8*)(attribute->value); return A71_SetCounter(HLSE_GET_OBJECT_INDEX(hObject), val); } } else return HLSE_ERR_API_ERROR; } //*************************** // HLSE_SM_KEY //*************************** else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_SM_KEYS) { if (attribute->value == NULL) return HLSE_ERR_API_ERROR; if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { HLSE_A71_SM_KEYS_DATA* data; if (attribute->valueLen != sizeof(HLSE_A71_SM_KEYS_DATA) || attribute->value == NULL) return HLSE_ERR_API_ERROR; data = (HLSE_A71_SM_KEYS_DATA*)(attribute->value); return SCP_GP_PutKeys(data->keyVersion, data->keyEnc, data->keyMac, data->keyDek, data->currentKeyDek, 16); } else return HLSE_ERR_API_ERROR; } //*************************** // HLSE_CERTIFICATE/DATA //*************************** else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_CERTIFICATE || HLSE_GET_OBJECT_TYPE(hObject) == HLSE_DATA) { if (attribute->value == NULL) return HLSE_ERR_API_ERROR; if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { if (attribute->valueLen == 0 || attribute->value == NULL) return HLSE_ERR_API_ERROR; return UpdateObjectData(HLSE_GET_OBJECT_INDEX(hObject), HLSE_GET_LOGICAL_OBJECT_CLASS(hObject), (U8*)(attribute->value), attribute->valueLen); } else if (attribute->type == HLSE_ATTR_DIRECT_ACCESS_OBJECT_VALUE && HLSE_GET_OBJECT_TYPE(hObject) == HLSE_DATA) { HLSE_DIRECT_ACCESS_ATTRIBUTE_VALUE* params; if (attribute->value == NULL || attribute->valueLen != sizeof(HLSE_DIRECT_ACCESS_ATTRIBUTE_VALUE)) return HLSE_ERR_API_ERROR; params = (HLSE_DIRECT_ACCESS_ATTRIBUTE_VALUE*)attribute->value; return DirectAccessUpdateObjectData(HLSE_GET_OBJECT_INDEX(hObject), HLSE_GET_LOGICAL_OBJECT_CLASS(hObject), params->offset, params->buffer, params->bufferLen); } else return HLSE_ERR_API_ERROR; } //*************************** // HLSE_MODULE //*************************** else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_MODULE) { if (attribute->value == NULL) return HLSE_ERR_API_ERROR; if (attribute->type == HLSE_ATTR_MODULE_TRANSPORT_LOCK_STATE) { HLSE_LIFE_CYCLE_STATE lockMode; if (attribute->value == NULL) return HLSE_ERR_API_ERROR; lockMode = *((HLSE_LIFE_CYCLE_STATE*)attribute->value); if (lockMode == HLSE_MODULE_LOCKED) { return A71_LockModule(); } else if (lockMode == HLSE_MODULE_UNLOCKED) { HLSE_A71_MODULE_UNLOCK_PARAMS* params = ((HLSE_A71_MODULE_UNLOCK_PARAMS*)attribute->value); return A71_UnlockModule(params->response, sizeof(params->response)); } } else if (attribute->type == HLSE_ATTR_MODULE_INJECTION_LOCK_STATE) { HLSE_LIFE_CYCLE_STATE lockMode ; if (attribute->valueLen != sizeof(HLSE_LIFE_CYCLE_STATE) || attribute->value == NULL) return HLSE_ERR_API_ERROR; lockMode = *((HLSE_LIFE_CYCLE_STATE*)attribute->value); if (lockMode == HLSE_INJECT_LOCKED) { return A71_InjectLock(); } } else return HLSE_ERR_API_ERROR; } return HLSE_ERR_API_ERROR; } HLSE_RET_CODE HLSE_GetObjectAttribute(HLSE_OBJECT_HANDLE hObject, HLSE_ATTRIBUTE* attribute) { #ifndef HLSE_IGNORE_PARAM_CHECK // allow only specific type if (hObject == HLSE_ANY_TYPE) { return HLSE_ERR_API_ERROR; } if (attribute == NULL) { return HLSE_ERR_API_ERROR; } #endif // { Check if user requets to obtain the length only if (attribute->value == NULL) { // return in attribute->valueLen a number of bytes which would suffice to hold the value to be returned if (attribute->type == HLSE_ATTR_OBJECT_TYPE) { attribute->valueLen = sizeof(HLSE_OBJECT_TYPE); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_OBJECT_INDEX) { attribute->valueLen = sizeof(HLSE_OBJECT_INDEX); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_READ_ONLY) { attribute->valueLen = 1; return HLSE_SW_OK; } else if ((attribute->type == HLSE_ATTR_MODULE_TOTAL_GP_SIZE) || (attribute->type == HLSE_ATTR_MODULE_APPLET_INFO)) { attribute->valueLen = sizeof(U16); return HLSE_SW_OK; } else if ((attribute->type == HLSE_ATTR_MODULE_TRANSPORT_LOCK_STATE) || (attribute->type == HLSE_ATTR_MODULE_SCP_LOCK_STATE) || (attribute->type == HLSE_ATTR_MODULE_INJECTION_LOCK_STATE) || (attribute->type == HLSE_ATTR_MODULE_RESTRICTED_KEYPAIR_INDEX)) { attribute->valueLen = sizeof(U8); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_UNIQUE_ID) { attribute->valueLen = A71CH_MODULE_UNIQUE_ID_LEN; return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_UNLOCK_CHALLENGE) { attribute->valueLen = A71CH_MODULE_UNLOCK_CHALLENGE_LEN; return HLSE_SW_OK; } else if ((attribute->type == HLSE_ATTR_MODULE_FREE_PERSISTENT_MEM) || (attribute->type == HLSE_ATTR_MODULE_FREE_TRANSIENT_MEM)) { attribute->valueLen = sizeof(short); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_APPLET_NAME) { attribute->valueLen = 5; return HLSE_SW_OK; } else if (((HLSE_GET_OBJECT_TYPE(hObject) == HLSE_KEY_PAIR) || (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_PUBLIC_KEY)) && (attribute->type == HLSE_ATTR_OBJECT_VALUE)) { attribute->valueLen = A71CH_PUB_KEY_LEN; // at least 65 bytes return HLSE_SW_OK; } else if ((HLSE_GET_OBJECT_TYPE(hObject) == HLSE_COUNTER) && (attribute->type == HLSE_ATTR_OBJECT_VALUE)) { attribute->valueLen = sizeof(U32); return HLSE_SW_OK; } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_CERTIFICATE || HLSE_GET_OBJECT_TYPE(hObject) == HLSE_DATA) { // length will be retrieved later on GetObjectData() } else { // type requested not found return HLSE_ERR_API_ERROR; } } // } end section obtaining only the length if (attribute->type == HLSE_ATTR_OBJECT_TYPE) { if (attribute->valueLen >= sizeof(HLSE_OBJECT_TYPE)) { HLSE_OBJECT_TYPE type = HLSE_GET_OBJECT_TYPE(hObject); memcpy(attribute->value, &type, sizeof(HLSE_OBJECT_TYPE)); attribute->valueLen = sizeof(HLSE_OBJECT_TYPE); return HLSE_SW_OK; } else { return HLSE_ERR_BUF_TOO_SMALL; } } else if (attribute->type == HLSE_ATTR_OBJECT_INDEX) { if (attribute->valueLen >= sizeof(HLSE_OBJECT_INDEX)) { HLSE_OBJECT_INDEX index = HLSE_GET_OBJECT_FULL_INDEX(hObject); memcpy(attribute->value, &index, sizeof(HLSE_OBJECT_INDEX)); attribute->valueLen = sizeof(HLSE_OBJECT_INDEX); return HLSE_SW_OK; } else { return HLSE_ERR_BUF_TOO_SMALL; } } else if (attribute->type == HLSE_ATTR_READ_ONLY) { if (attribute->valueLen >= sizeof(U8)) { U8 objState = ((hObject & 0x80000000) ? 1 : 0); memcpy(attribute->value, &objState, 1); attribute->valueLen = 1; return HLSE_SW_OK; } else { return HLSE_ERR_BUF_TOO_SMALL; } } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_MODULE) { if ((attribute->type == HLSE_ATTR_MODULE_TOTAL_GP_SIZE && attribute->valueLen < sizeof(U16)) || (attribute->type == HLSE_ATTR_MODULE_TRANSPORT_LOCK_STATE && attribute->valueLen < sizeof(U8)) || (attribute->type == HLSE_ATTR_MODULE_SCP_LOCK_STATE && attribute->valueLen < sizeof(U8)) || (attribute->type == HLSE_ATTR_MODULE_INJECTION_LOCK_STATE && attribute->valueLen < sizeof(U8)) || (attribute->type == HLSE_ATTR_MODULE_APPLET_INFO && attribute->valueLen < sizeof(U16))) { return HLSE_ERR_BUF_TOO_SMALL; } if (attribute->type == HLSE_ATTR_MODULE_TOTAL_GP_SIZE || attribute->type == HLSE_ATTR_MODULE_TRANSPORT_LOCK_STATE || attribute->type == HLSE_ATTR_MODULE_SCP_LOCK_STATE || attribute->type == HLSE_ATTR_MODULE_INJECTION_LOCK_STATE || attribute->type == HLSE_ATTR_MODULE_APPLET_INFO || attribute->type == HLSE_ATTR_MODULE_RESTRICTED_KEYPAIR_INDEX ) { U16 selectResponse = 0; U8 debugOn = 0; U8 restrictedKpIdx = 0; U8 transportLockState = 0; U8 scpState = 0; U8 injectLockState = 0; U16 gpStorageSize = 0; U16 lReturn ; attribute->valueLen = sizeof(S16); lReturn = A71_GetModuleInfo(&selectResponse, &debugOn, &restrictedKpIdx, &transportLockState, &scpState, &injectLockState, &gpStorageSize); if (lReturn != SW_OK) return lReturn; if (attribute->type == HLSE_ATTR_MODULE_TOTAL_GP_SIZE) { attribute->valueLen = sizeof(S16); memcpy(attribute->value, &gpStorageSize, sizeof(short)); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_TRANSPORT_LOCK_STATE) { attribute->valueLen = sizeof(U8); memcpy(attribute->value, &transportLockState, sizeof(U8)); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_SCP_LOCK_STATE) { attribute->valueLen = sizeof(U8); memcpy(attribute->value, &scpState, sizeof(U8)); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_INJECTION_LOCK_STATE) { attribute->valueLen = sizeof(U8); memcpy(attribute->value, &injectLockState, sizeof(U8)); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_APPLET_INFO) { attribute->valueLen = sizeof(U16); memcpy(attribute->value, &selectResponse, sizeof(U16)); return HLSE_SW_OK; } else if (attribute->type == HLSE_ATTR_MODULE_RESTRICTED_KEYPAIR_INDEX) { attribute->valueLen = sizeof(U8); memcpy(attribute->value, &restrictedKpIdx, sizeof(U8)); return HLSE_SW_OK; } } else { if (attribute->type == HLSE_ATTR_MODULE_UNIQUE_ID) { return A71_GetUniqueID(attribute->value, &(attribute->valueLen)); } else if (attribute->type == HLSE_ATTR_MODULE_UNLOCK_CHALLENGE) { return A71_GetUnlockChallenge(attribute->value, &(attribute->valueLen)); } else if (attribute->type == HLSE_ATTR_MODULE_RANDOM) { return A71_GetRandom(attribute->value, (U8)attribute->valueLen); } else if (attribute->type == HLSE_ATTR_MODULE_CREDENTIAL_INFO) { return A71_GetCredentialInfo(attribute->value, &attribute->valueLen); } else if (attribute->type == HLSE_ATTR_MODULE_FREE_PERSISTENT_MEM) { if (attribute->valueLen >= sizeof(short)) { attribute->valueLen = sizeof(short); return A71_DbgGetFreePersistentMemory((short*)attribute->value); } else { return HLSE_ERR_BUF_TOO_SMALL; } } else if (attribute->type == HLSE_ATTR_MODULE_FREE_TRANSIENT_MEM) { if (attribute->valueLen >= sizeof(S16)) { attribute->valueLen = sizeof(S16); return A71_DbgGetFreeTransientMemory((S16*)attribute->value); } else { return HLSE_ERR_BUF_TOO_SMALL; } } else if (attribute->type == HLSE_ATTR_MODULE_APPLET_NAME && attribute->valueLen >= 5) { U8 appletAID[] = APPLET_NAME; memcpy(attribute->value, appletAID, sizeof(appletAID)); attribute->valueLen = APPLET_NAME_LEN; return HLSE_SW_OK; } } return HLSE_ERR_API_ERROR; } else { if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_KEY_PAIR) { if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { return A71_GetPublicKeyEccKeyPair(HLSE_GET_OBJECT_INDEX(hObject), attribute->value, &(attribute->valueLen)); } } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_PUBLIC_KEY) { if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { return A71_GetEccPublicKey(HLSE_GET_OBJECT_INDEX(hObject), attribute->value, &(attribute->valueLen)); } } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_COUNTER) { if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { U32 counter = 0; A71_GetCounter(HLSE_GET_OBJECT_INDEX(hObject), &counter); memcpy(attribute->value, &counter, sizeof(U32)); attribute->valueLen = sizeof(U32); return HLSE_SW_OK; } } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_CERTIFICATE || HLSE_GET_OBJECT_TYPE(hObject) == HLSE_DATA) { if (attribute->type == HLSE_ATTR_OBJECT_VALUE) { return GetObjectData(HLSE_GET_OBJECT_INDEX(hObject), HLSE_GET_LOGICAL_OBJECT_CLASS(hObject), attribute->value, &attribute->valueLen); } else if (attribute->type == HLSE_ATTR_OBJECT_OFFSET) { // fetch object offset from Gp table HLSE_RET_CODE lReturn = HLSE_SW_OK; U16 offset; // to be fetched U16 length, availLength; // dummy args lReturn = GetObjectOffsetAndLength(HLSE_GET_OBJECT_INDEX(hObject), HLSE_GET_LOGICAL_OBJECT_CLASS(hObject), &offset, &length, &availLength); if (lReturn != HLSE_SW_OK) { return lReturn; } if (attribute->value == NULL) { return HLSE_ERR_MEMORY; } memcpy(attribute->value, &offset, sizeof(offset)); attribute->valueLen = sizeof(offset); return lReturn; } else if (attribute->type == HLSE_ATTR_DIRECT_ACCESS_OBJECT_VALUE && HLSE_GET_OBJECT_TYPE(hObject) == HLSE_DATA) { HLSE_DIRECT_ACCESS_ATTRIBUTE_VALUE* params; if (attribute->value == NULL || attribute->valueLen != sizeof(HLSE_DIRECT_ACCESS_ATTRIBUTE_VALUE)) return HLSE_ERR_API_ERROR; params = (HLSE_DIRECT_ACCESS_ATTRIBUTE_VALUE*)attribute->value; return DirectAccessGetObjectData(HLSE_GET_OBJECT_INDEX(hObject), HLSE_GET_LOGICAL_OBJECT_CLASS(hObject), params->offset, params->bytes, params->buffer, ¶ms->bufferLen); } } } return HLSE_ERR_API_ERROR; } HLSE_RET_CODE HLSE_CreateObject(HLSE_ATTRIBUTE* attributes, U16 attributesNum, HLSE_OBJECT_HANDLE* hObject) { // Get the object type and index and create the handle HLSE_OBJECT_TYPE objType = HLSE_ANY_TYPE; HLSE_OBJECT_INDEX objIndex = 0; U16 valAttrIndex = 0; U8 readOnly = 0; HLSE_RET_CODE lReturn = HLSE_SW_OK; HLSE_GP_DATA_TABLE_ENTRY entry = { 0 }; U16 i; #ifndef HLSE_IGNORE_PARAM_CHECK // HLSE_ATTR_OBJECT_TYPE and HLSE_ATTR_OBJECT_INDEX must appear if (attributes == NULL || hObject == NULL || attributesNum < 2) return HLSE_ERR_API_ERROR; #endif for (i = 0; i < attributesNum; ++i) { if (attributes[i].type == HLSE_ATTR_OBJECT_TYPE) { if (attributes[i].value == NULL || attributes[i].valueLen < 4) return HLSE_ERR_API_ERROR; objType = *(HLSE_OBJECT_TYPE*)(attributes[i].value); } else if (attributes[i].type == HLSE_ATTR_OBJECT_INDEX) { if (attributes[i].value == NULL || attributes[i].valueLen < 4) return HLSE_ERR_API_ERROR; objIndex = *(HLSE_OBJECT_INDEX*)(attributes[i].value); } else if (attributes[i].type == HLSE_ATTR_READ_ONLY) { if (attributes[i].value == NULL || attributes[i].valueLen < 1) return HLSE_ERR_API_ERROR; readOnly = *(U8*)(attributes[i].value); } else if (attributes[i].type == HLSE_ATTR_OBJECT_VALUE || attributes[i].type == HLSE_ATTR_WRAPPED_OBJECT_VALUE) { valAttrIndex = i; } } *hObject = HLSE_CREATE_HANDLE(readOnly, objType, objIndex); // Create the table entry and the object handle , and set the object value in GP storage if (objType == HLSE_CERTIFICATE || objType == HLSE_DATA) { // verify new object to be created has valid len and value if (attributes[valAttrIndex].type == HLSE_ATTR_OBJECT_VALUE) { if (attributes[valAttrIndex].valueLen == 0 || attributes[valAttrIndex].value == NULL) return HLSE_ERR_API_ERROR; } entry.index = (U8)objIndex; entry.klass = HLSE_GET_LOGICAL_OBJECT_CLASS(objType); entry.length = attributes[valAttrIndex].valueLen; lReturn = GetOffsetForNewObjectAndAddNewEntry(attributes[valAttrIndex].valueLen, &entry); if (lReturn != HLSE_SW_OK) { return lReturn; } // No lock check necessary here since we've done it in GetOffsetForNewObjectAndAddNewEntry() lReturn = A71_SetGpData(entry.offset, (U8*)(attributes[valAttrIndex].value), attributes[valAttrIndex].valueLen); if (lReturn != HLSE_SW_OK) { return lReturn; } } else { lReturn = HLSE_SetObjectAttribute(*hObject, &attributes[valAttrIndex]); if (lReturn != HLSE_SW_OK) { return lReturn; } } // if read only, freeze the data if (readOnly) { if (objType == HLSE_KEY_PAIR) { return A71_FreezeEccKeyPair((U8)objIndex); } else if (objType == HLSE_PUBLIC_KEY) { return A71_FreezeEccPublicKey((U8)objIndex); } else if (objType == HLSE_SYMMETRIC_KEY) { return A71_FreezeSymKey((U8)objIndex); } else if (objType == HLSE_CERTIFICATE || objType == HLSE_DATA) { U16 alignedLength = HLSE_ALIGN_SIZE(attributes[valAttrIndex].valueLen) * HLSE_GP_DATA_CHUNK; return A71_FreezeGpData(entry.offset, alignedLength); } } return lReturn; } HLSE_RET_CODE HLSE_EraseObject(HLSE_OBJECT_HANDLE hObject) { if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_KEY_PAIR) { return A71_EraseEccKeyPair(HLSE_GET_OBJECT_INDEX(hObject)); } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_PUBLIC_KEY) { return A71_EraseEccPublicKey(HLSE_GET_OBJECT_INDEX(hObject)); } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_SYMMETRIC_KEY) { return A71_EraseSymKey(HLSE_GET_OBJECT_INDEX(hObject)); } else if ((HLSE_GET_OBJECT_TYPE(hObject) == HLSE_CERTIFICATE) || (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_DATA)) { if ((hObject & 0x80000000)) { // HLSE_ATTR_READ_ONLY - means it is frozen - cannot be deleted return HLSE_ERR_API_ERROR; } else { return DeleteGPDataTableEntry(HLSE_GET_OBJECT_INDEX(hObject), HLSE_GET_LOGICAL_OBJECT_CLASS(hObject)); } } return HLSE_ERR_API_ERROR; } //******************************************************************* // Cryptographic Operations - defined in HLSECrypto.h //******************************************************************* HLSE_RET_CODE HLSE_GetSupportedMechanisms(HLSE_MECHANISM_TYPE* mechanism, U16* mechanismLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if (mechanismLen == NULL) return HLSE_ERR_API_ERROR; #endif if (mechanism == NULL) { *mechanismLen = 15; return HLSE_SW_OK; } if (mechanism != NULL && mechanismLen != NULL && *mechanismLen < 15) { *mechanismLen = 15; return HLSE_ERR_BUF_TOO_SMALL; } *mechanismLen = 15; *mechanism++ = HLSE_SHA1; *mechanism++ = HLSE_SHA256; *mechanism++ = HLSE_AES_CMAC; *mechanism++ = HLSE_TLS_1_2; *mechanism++ = HLSE_HKDF_HMAC_SHA256_EXTRACT_AND_EXPAND; *mechanism++ = HLSE_HKDF_HMAC_SHA256_SKIP_EXTRACT; *mechanism++ = HLSE_HMAC_AES_SHA256; *mechanism++ = HLSE_RFC3394_AES_KEY_WRAPPING; *mechanism++ = HLSE_TLS_PSK_MASTER_KEY_DERIVE; *mechanism++ = HLSE_TLS_PSK_MASTER_KEY_DERIVE_DH_ECC; *mechanism++ = HLSE_ECDH; *mechanism++ = HLSE_ECDSA_SIGN; *mechanism++ = HLSE_ECDSA_VERIFY; *mechanism++ = HLSE_TRNG; *mechanism++ = HLSE_SCP03_ALG; return HLSE_SW_OK; } HLSE_RET_CODE HLSE_GetSupportedMechanismsForObject(HLSE_OBJECT_HANDLE hObject, HLSE_MECHANISM_TYPE* mechanism, U16* mechanismLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if (mechanismLen == NULL) return HLSE_ERR_API_ERROR; #endif if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_KEY_PAIR) { if (mechanism == NULL) { *mechanismLen = 4; return HLSE_SW_OK; } if (mechanism != NULL && mechanismLen != NULL && *mechanismLen < 4) { *mechanismLen = 4; return HLSE_ERR_BUF_TOO_SMALL; } *mechanismLen = 4; *mechanism++ = HLSE_TLS_PSK_MASTER_KEY_DERIVE_DH_ECC; *mechanism++ = HLSE_ECDH; *mechanism++ = HLSE_ECDSA_SIGN; *mechanism++ = HLSE_ECDSA_VERIFY; } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_PUBLIC_KEY) { if (mechanism == NULL) { *mechanismLen = 1; return HLSE_SW_OK; } if (mechanism != NULL && mechanismLen != NULL && *mechanismLen < 1) { *mechanismLen = 1; return HLSE_ERR_BUF_TOO_SMALL; } *mechanismLen = 1; *mechanism++ = HLSE_ECDSA_VERIFY; } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_SYMMETRIC_KEY) { if (mechanism == NULL) { *mechanismLen = 4; return HLSE_SW_OK; } if (mechanism != NULL && mechanismLen != NULL && *mechanismLen < 4) { *mechanismLen = 4; return HLSE_ERR_BUF_TOO_SMALL; } *mechanismLen = 4; *mechanism++ = HLSE_AES_CMAC; *mechanism++ = HLSE_HKDF_HMAC_SHA256_EXTRACT_AND_EXPAND; *mechanism++ = HLSE_HKDF_HMAC_SHA256_SKIP_EXTRACT; *mechanism++ = HLSE_HMAC_AES_SHA256; } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_CONFIG_KEY) { if (mechanism == NULL) { *mechanismLen = 1; return HLSE_SW_OK; } if (mechanism != NULL && mechanismLen != NULL && *mechanismLen < 1) { *mechanismLen = 1; return HLSE_ERR_BUF_TOO_SMALL; } *mechanismLen = 1; *mechanism++ = HLSE_RFC3394_AES_KEY_WRAPPING; } else if (HLSE_GET_OBJECT_TYPE(hObject) == HLSE_SM_KEYS) { if (mechanism == NULL) { *mechanismLen = 1; return HLSE_SW_OK; } if (mechanism != NULL && mechanismLen != NULL && *mechanismLen < 1) { *mechanismLen = 1; return HLSE_ERR_BUF_TOO_SMALL; } *mechanismLen = 1; *mechanism++ = HLSE_SCP03; } return HLSE_ERR_API_ERROR; } //HLSE_CONTEXT_HANDLE HLSE_RET_CODE HLSE_Digest(HLSE_MECHANISM_INFO* pMechanismType, U8* inData, U16 inDataLen, U8* outDigest, U16* outDigestLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if ((pMechanismType == NULL) || inData == NULL) { return HLSE_ERR_API_ERROR; } #endif // Check for a request for digest Len if (outDigest == NULL) { if (pMechanismType->mechanism == HLSE_SHA256) { *outDigestLen = 32; return HLSE_SW_OK; } else { return HLSE_ERR_API_ERROR; } } if (pMechanismType->mechanism == HLSE_SHA256) { return A71_GetSha256(inData, (U16)inDataLen, outDigest, outDigestLen); } return HLSE_ERR_API_ERROR; } //HLSE_RET_CODE HLSE_DigestInit(HLSE_MECHANISM_INFO* pMechanismType, HLSE_CONTEXT_HANDLE* hContext) //{ // //} // //HLSE_RET_CODE HLSE_DigestUpdate(HLSE_CONTEXT_HANDLE hContext, U8* inDataPart, U16 inDataPartLen) //{ // //} // //HLSE_RET_CODE HLSE_DigestFinal(HLSE_CONTEXT_HANDLE hContext, U8* outDigest, U16* outDigestLen) //{ // //} HLSE_RET_CODE HLSE_Sign(HLSE_MECHANISM_INFO* pMechanismType, HLSE_OBJECT_HANDLE hObject, U8* inData, U16 inDataLen, U8* outSignature, U16* outSignatureLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if ((pMechanismType == NULL) || (inData == NULL)) { return HLSE_ERR_API_ERROR; } #endif // Check for a request to get the required signature len if (outSignature == NULL) { if (pMechanismType->mechanism == HLSE_HMAC_AES_SHA256) { *outSignatureLen = 32; return HLSE_SW_OK; } else if (pMechanismType->mechanism == HLSE_ECDSA_SIGN) { *outSignatureLen = 128; return HLSE_SW_OK; } else if (pMechanismType->mechanism == HLSE_ECDSA_NORMALIZE_ASN_SIGN) { *outSignatureLen = 128; return HLSE_SW_OK; } else { return HLSE_ERR_API_ERROR; } } if (pMechanismType->mechanism == HLSE_HMAC_AES_SHA256) { HLSE_HKDF_PARAMS* params = (HLSE_HKDF_PARAMS*)pMechanismType->pParameter; return A71_GetHmacSha256(HLSE_GET_OBJECT_INDEX(hObject), params->nBlock, inData, (U16)inDataLen, outSignature, outSignatureLen); } else if (pMechanismType->mechanism == HLSE_ECDSA_SIGN) { return A71_EccSign(HLSE_GET_OBJECT_INDEX(hObject), inData, (U16)inDataLen, outSignature, outSignatureLen); } else if (pMechanismType->mechanism == HLSE_ECDSA_NORMALIZE_ASN_SIGN) { return A71_EccNormalizedAsnSign(HLSE_GET_OBJECT_INDEX(hObject), inData, (U16)inDataLen, outSignature, outSignatureLen); } return HLSE_ERR_API_ERROR; } HLSE_RET_CODE HLSE_VerifySignature(HLSE_MECHANISM_INFO* pMechanismType, HLSE_OBJECT_HANDLE hObject, U8* inData, U16 inDataLen, U8* inSignature, U16 inSignatureLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if ((pMechanismType == NULL) || (inData == NULL) || (inSignature == NULL)) { return HLSE_ERR_API_ERROR; } #endif if (pMechanismType->mechanism == HLSE_ECDSA_VERIFY) { U8 result = 0; HLSE_RET_CODE lReturn = 0; lReturn = A71_EccVerify(HLSE_GET_OBJECT_INDEX(hObject), inData, (U16)inDataLen, inSignature, inSignatureLen, &result); if (lReturn != HLSE_SW_OK) return lReturn; if (result == 0) // fail return HLSE_ERR_GENERAL_ERROR; return HLSE_SW_OK; } return HLSE_ERR_API_ERROR; } HLSE_RET_CODE HLSE_VerifySignatureWithExternalKey(HLSE_MECHANISM_INFO* pMechanismType, U8* inExtKey, U16 inExtKeyLen, U8* inData, U16 inDataLen, U8* inSignature, U16 inSignatureLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if ((pMechanismType == NULL) || (inData == NULL) || (inSignature == NULL)) { return HLSE_ERR_API_ERROR; } #endif if (pMechanismType->mechanism == HLSE_ECDSA_VERIFY) { U8 result = 0; HLSE_RET_CODE lReturn = 0; lReturn = A71_EccVerifyWithKey(inExtKey, (U16)inExtKeyLen, inData, (U16)inDataLen, inSignature, (U16)inSignatureLen, &result); if (lReturn != HLSE_SW_OK) return lReturn; if (result == 0) // fail return HLSE_ERR_GENERAL_ERROR; return HLSE_SW_OK; } return HLSE_ERR_API_ERROR; } // NOTE: all the data required for the key derivation (except for the key itself that is specified by hObject) is conveyed in the mechanism HLSE_RET_CODE HLSE_DeriveKey(HLSE_MECHANISM_INFO* pMechanismType, HLSE_OBJECT_HANDLE hObject, U8* outDerivedKey, U16* outDerivedKeyLen) { // TODO/verify ulParameterLen is correct!!! #ifndef HLSE_IGNORE_PARAM_CHECK if ((pMechanismType == NULL) || (outDerivedKeyLen == NULL)) { return HLSE_ERR_API_ERROR; } #endif // Check for a request to get outDerivedKeyLen if (outDerivedKey == NULL) { if (pMechanismType->mechanism == HLSE_HKDF_HMAC_SHA256_EXTRACT_AND_EXPAND) { *outDerivedKeyLen = 32; return HLSE_SW_OK; } else if (pMechanismType->mechanism == HLSE_HKDF_HMAC_SHA256_SKIP_EXTRACT) { *outDerivedKeyLen = DERIVE_KEYDATA_FROM_SHARED_SECRET_MAX_DERIVED_DATA; // 255 return HLSE_SW_OK; } else if (pMechanismType->mechanism == HLSE_TLS_PSK_MASTER_KEY_DERIVE) { *outDerivedKeyLen = 48; return HLSE_SW_OK; } else if (pMechanismType->mechanism == HLSE_TLS_PSK_MASTER_KEY_DERIVE_DH_ECC) { *outDerivedKeyLen = 48; return HLSE_SW_OK; } else if (pMechanismType->mechanism == HLSE_ECDH) { *outDerivedKeyLen = 256; return HLSE_SW_OK; } else { return HLSE_ERR_API_ERROR; } } if (pMechanismType->mechanism == HLSE_HKDF_HMAC_SHA256_EXTRACT_AND_EXPAND) { HLSE_HKDF_PARAMS* params = (HLSE_HKDF_PARAMS*)pMechanismType->pParameter; return A71_HkdfExpandSymKey(HLSE_GET_OBJECT_INDEX(hObject), params->nBlock, params->pInfo, (U16)params->ulInfoLen, outDerivedKey, *outDerivedKeyLen); } else if (pMechanismType->mechanism == HLSE_HKDF_HMAC_SHA256_SKIP_EXTRACT) { HLSE_HKDF_PARAMS* params = (HLSE_HKDF_PARAMS*)pMechanismType->pParameter; return A71_HkdfSymKey(HLSE_GET_OBJECT_INDEX(hObject), params->nBlock, params->pSalt, (U16)params->ulSaltLen, params->pInfo, (U16)params->ulInfoLen, outDerivedKey, *outDerivedKeyLen); } else if (pMechanismType->mechanism == HLSE_TLS_PSK_MASTER_KEY_DERIVE) { HLSE_TLS_PSK_MASTER_KEY_DERIVE_PARAMS* params = (HLSE_TLS_PSK_MASTER_KEY_DERIVE_PARAMS*)pMechanismType->pParameter; return A71_PskDeriveMasterSecret(HLSE_GET_OBJECT_INDEX(hObject), params->nBlock, params->pSeed, (U16)params->ulSeedLen, outDerivedKey); } else if (pMechanismType->mechanism == HLSE_TLS_PSK_MASTER_KEY_DERIVE_DH_ECC) { HLSE_TLS_PSK_MASTER_KEY_DERIVE_DH_ECC_PARAMS* params = (HLSE_TLS_PSK_MASTER_KEY_DERIVE_DH_ECC_PARAMS*)pMechanismType->pParameter; return A71_EcdhPskDeriveMasterSecret(HLSE_GET_OBJECT_INDEX(hObject), params->pPublicKey, (U16)params->ulPublicKeyLen, HLSE_GET_OBJECT_INDEX(params->symKeyHandle), params->numBlocks, params->pSeed, (U16)params->ulSeedLen, outDerivedKey); } else if (pMechanismType->mechanism == HLSE_ECDH) { HLSE_ECDH_PARAMS* params = (HLSE_ECDH_PARAMS*)pMechanismType->pParameter; return A71_EcdhGetSharedSecret(HLSE_GET_OBJECT_INDEX(hObject), params->pPublicKey, (U16)params->ulPublicKeyLen, outDerivedKey, outDerivedKeyLen); } return HLSE_ERR_API_ERROR; } HLSE_RET_CODE HLSE_Encrypt(HLSE_MECHANISM_INFO* pMechanismType, HLSE_OBJECT_HANDLE hObject, U8* inData, U16 inDataLen, U8* outData, U16* outDataLen) { return HLSE_ERR_NOT_SUPPORTED; } HLSE_RET_CODE HLSE_Decrypt(HLSE_MECHANISM_INFO* pMechanismType, HLSE_OBJECT_HANDLE hObject, U8* inData, U16 inDataLen, U8* outData, U16* outDataLen) { return HLSE_ERR_NOT_SUPPORTED; } //CK_ECDH1_DERIVE_PARAMS params; //CK_MECHANISM mechanism = { CKM_ECDH1_DERIVE, ¶ms, sizeof(params) }; //******************************************************************* // Module Operations - defined in HLSEMisc.h //******************************************************************* // Debug functions HLSE_RET_CODE HLSE_DbgDisableDebug() { return A71_DbgDisableDebug(); } HLSE_RET_CODE HLSE_DbgReflect(U8* inData, U16 inDataLen, U8* outData, U16* outDataLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if ((inData == NULL) || (outDataLen == NULL)) { return HLSE_ERR_API_ERROR; } #endif return A71_DbgReflect(inData, (U16)inDataLen, outData, outDataLen); } HLSE_RET_CODE HLSE_DbgReset() { // the cached gp table no lnger reflects the actual data in the GP storage and has to be re-read gMappingTableRead = 0; return A71_DbgReset(); } //******************************************************************* // Communication and Secure Channel - defined in HLSEComm.h //******************************************************************* HLSE_RET_CODE HLSE_CloseConnection(HLSE_CLOSE_CONNECTION_MODE mode) { if ((mode != HLSE_CLOSE_CONNECTION_RESET) && (mode != HLSE_CLOSE_CONNECTION_NO_RESET)) { return HLSE_ERR_API_ERROR; } return SM_Close(NULL, (U8)mode); } HLSE_RET_CODE HLSE_Connect(HLSE_CONNECTION_PARAMS* params, HLSE_COMMUNICATION_STATE *commState) { #if defined(SMCOM_JRCP_V1) || defined(SMCOM_JRCP_V2) || defined(RJCT_VCOM) SmCommState_t a71SmCommState = {0}; a71SmCommState.connType = params->connType; U16 lReturn; #ifndef HLSE_IGNORE_PARAM_CHECK if (params == NULL || commState == NULL || params->pParameter == NULL) { return HLSE_ERR_API_ERROR; } #endif lReturn = SM_RjctConnect(NULL, (const char *)params->pParameter, &a71SmCommState, commState->atr, &(commState->atrLen)); if (lReturn != SW_OK) return lReturn; memcpy(commState, &a71SmCommState, sizeof(a71SmCommState)); return HLSE_SW_OK; #else SmCommState_t a71SmCommState = {0}; U16 lReturn; #ifndef HLSE_IGNORE_PARAM_CHECK if (params == NULL || commState == NULL ) { return HLSE_ERR_API_ERROR; } #endif lReturn = SM_Connect(NULL, &a71SmCommState, commState->atr, &(commState->atrLen)); if (lReturn != SW_OK) { return lReturn; } memcpy(commState, &a71SmCommState, sizeof(a71SmCommState)); return HLSE_SW_OK; #endif } HLSE_RET_CODE HLSE_ResumeConnection(HLSE_COMMUNICATION_STATE *commState, HLSE_SECURE_CHANNEL_SESSION_STATE *smState) { #ifndef HLSE_IGNORE_PARAM_CHECK if (commState == NULL || smState == NULL ) { return HLSE_ERR_API_ERROR; } #endif if (smState->type != HLSE_SCP03 || smState->ulParameterLen != sizeof(HLSE_SCP03_SESSION_STATE)) return HLSE_ERR_MEMORY; return SM_ResumeConnection((SmCommState_t *)commState, (Scp03SessionState_t*)(smState->pParameter)); } HLSE_RET_CODE HLSE_SendAPDU(U8 *cmd, U16 cmdLen, U8 *resp, U16 *respLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if (cmd == NULL || resp == NULL) { return HLSE_ERR_API_ERROR; } #endif return SM_SendAPDU(cmd, cmdLen, resp, respLen); } HLSE_RET_CODE HLSE_SCP_Subscribe(HLSE_SCP_SignalFunction callback, void *context) { #ifndef HLSE_IGNORE_PARAM_CHECK if (callback == NULL) { return HLSE_ERR_API_ERROR; } #endif return SCP_Subscribe((SCP_SignalFunction)callback, context); } HLSE_RET_CODE HLSE_SMChannelAuthenticate(HLSE_SECURE_CHANNEL_ESTABLISH_PARAMS* params, HLSE_SECURE_CHANNEL_STATE* channelState) { #if defined(USE_HOSTCRYPTO_FOR_SCP03) HLSE_SECURE_CHANNEL_SCP03_ESTABLISH_PARAMS* scp03EstablishParams; #endif #if defined(USE_HOSTCRYPTO_FOR_SCP03) HLSE_SCP03_CHANNEL_STATE *scp03ChannelState; U16 counterLen = 16; #endif U16 lReturn = ERR_COMM_ERROR; #ifndef HLSE_IGNORE_PARAM_CHECK if (params == NULL || channelState == NULL) { return HLSE_ERR_API_ERROR; } #if defined(USE_HOSTCRYPTO_FOR_SCP03) if (params->type != HLSE_SCP03 || params->pParameter == NULL || params->ulParameterLen != sizeof(HLSE_SECURE_CHANNEL_SCP03_ESTABLISH_PARAMS)) return HLSE_ERR_MEMORY; #endif if (channelState->type != HLSE_SCP03 || channelState->pParameter == NULL || channelState->ulParameterLen != sizeof(HLSE_SCP03_CHANNEL_STATE)) return HLSE_ERR_MEMORY; #endif /* HLSE_IGNORE_PARAM_CHECK */ #if defined(USE_HOSTCRYPTO_FOR_SCP03) scp03EstablishParams = (HLSE_SECURE_CHANNEL_SCP03_ESTABLISH_PARAMS*)(params->pParameter); #endif #if defined(USE_HOSTCRYPTO_FOR_SCP03) scp03ChannelState = (HLSE_SCP03_CHANNEL_STATE*)(channelState->pParameter); #endif #if defined(USE_HOSTCRYPTO_FOR_SCP03) lReturn = SCP_Authenticate(scp03EstablishParams->keyEnc, scp03EstablishParams->keyMac, scp03EstablishParams->keyDek, 16, scp03ChannelState->cCounter, &counterLen); if (lReturn == SW_OK) { channelState->ulParameterLen = counterLen; } #endif return lReturn; } HLSE_RET_CODE HLSE_SMChannelGetScpSessionState(HLSE_SECURE_CHANNEL_SESSION_STATE *channelState) { #ifndef HLSE_IGNORE_PARAM_CHECK if (channelState == NULL) { return HLSE_ERR_API_ERROR; } if (channelState->type != HLSE_SCP03 || channelState->pParameter == NULL || channelState->ulParameterLen != sizeof(HLSE_SCP03_SESSION_STATE)) return HLSE_ERR_MEMORY; #endif return SCP_GetScpSessionState((Scp03SessionState_t *)(channelState->pParameter)); } HLSE_RET_CODE HLSE_SMChannelSetScpSessionState(HLSE_SECURE_CHANNEL_SESSION_STATE *channelState) { #ifndef HLSE_IGNORE_PARAM_CHECK if (channelState == NULL) { return HLSE_ERR_API_ERROR; } #endif if (channelState->type != HLSE_SCP03 || channelState->pParameter == NULL || channelState->ulParameterLen != sizeof(HLSE_SCP03_SESSION_STATE)) return HLSE_ERR_MEMORY; SCP_SetScpSessionState((Scp03SessionState_t *)(channelState->pParameter)); return HLSE_SW_OK; } //******************************************************************* // Helper functions //******************************************************************* //HLSE_RET_CODE HLSE_WrapKey(inKeyData, wrappingKeyIndex, outData); HLSE_RET_CODE HLSE_NormalizeECCSignature(U8 *signature, U16 signatureLen, U8 *normalizedSignature, U16 *normalizedSignatureLen) { #ifndef HLSE_IGNORE_PARAM_CHECK if (signature == NULL || normalizedSignature == NULL || normalizedSignatureLen == NULL) { return HLSE_ERR_API_ERROR; } #endif // prepare in/out parameters memcpy(normalizedSignature, signature, signatureLen); *normalizedSignatureLen = signatureLen; return hcNormalizeAsnSignatureEcc(normalizedSignature, normalizedSignatureLen); } //******************************************************************* // For testing or Debug Use only //******************************************************************* HLSE_RET_CODE Debug_ForceReadGPDataTable() { HLSE_GP_DATA_TABLE table; return ReadGPDataTable(&table, 1); } #endif /* SSS_HAVE_A71CH || SSS_HAVE_A71CH_SIM */