blob: 10ef931fad8c6f8c5cc753108dbd54e987bfd557 [file] [log] [blame]
/**
* @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 <stdlib.h>
#include "HLSEAPI.h"
#include "ax_api.h"
#include "a71_debug.h"
#include "hcAsn.h"
#include "sm_apdu.h"
#include <sm_const.h>
#include <ax_sss_scp.h>
#include <a71ch_api.h>
#include <a71_debug.h>
#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, &params->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, &params, 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 */