/**
 * @file   tlcTeeKeymaster_if.c
 * @brief  Contains trustlet connector interface implementations to
 * handle key operations with TEE Keymaster trustlet
 *
 * Copyright Giesecke & Devrient GmbH 2012
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdlib.h>

#include "MobiCoreDriverApi.h"
#include "tlTeeKeymaster_Api.h"
#include "tlcTeeKeymaster_log.h"
#include "tlcTeeKeymaster_if.h"


/* Global definitions */
static const uint32_t DEVICE_ID = MC_DEVICE_ID_DEFAULT;
static const mcUuid_t uuid = TEE_KEYMASTER_TL_UUID;

/**
 * TEE_Open
 *
 * Open session to the TEE Keymaster trustlet
 *
 * @param  pSessionHandle  [out] Return pointer to the session handle
 */
static tciMessage_ptr TEE_Open(
    mcSessionHandle_t *pSessionHandle
){
    tciMessage_ptr pTci = NULL;
    mcResult_t     mcRet;

    do
    {

        /* Validate session handle */
        if (!pSessionHandle)
        {
            LOG_E("TEE_Open(): Invalid session handle\n");
            break;
        }

        /* Initialize session handle data */
        bzero(pSessionHandle, sizeof(mcSessionHandle_t));

        /* Open MobiCore device */
        mcRet = mcOpenDevice(DEVICE_ID);
        if (MC_DRV_OK != mcRet)
        {
            LOG_E("TEE_Open(): mcOpenDevice returned: %d\n", mcRet);
            break;
        }

        /* Allocating WSM for TCI */
        mcRet = mcMallocWsm(DEVICE_ID, 0, sizeof(tciMessage_t), (uint8_t **) &pTci, 0);
        if (MC_DRV_OK != mcRet)
        {
            LOG_E("TEE_Open(): mcMallocWsm returned: %d\n", mcRet);
            break;
        }

        /* Open session the TEE Keymaster trustlet */
        pSessionHandle->deviceId = DEVICE_ID;
        mcRet = mcOpenSession(pSessionHandle,
                              &uuid,
                              (uint8_t *) pTci,
                              (uint32_t) sizeof(tciMessage_t));
        if (MC_DRV_OK != mcRet)
        {
            LOG_E("TEE_Open(): mcOpenSession returned: %d\n", mcRet);
            break;
        }

    } while (false);

    return pTci;
}


/**
 * TEE_Close
 *
 * Close session to the TEE Keymaster trustlet
 *
 * @param  sessionHandle  [in] Session handle
 */
static void TEE_Close(
    mcSessionHandle_t sessionHandle
){
    teeResult_t   ret = TEE_ERR_NONE;
    mcResult_t    mcRet;

    do {

        /* Close session */
        mcRet = mcCloseSession(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            LOG_E("TEE_Close(): mcCloseSession returned: %d\n", mcRet);
            ret = TEE_ERR_SESSION;
            break;
        }

        /* Close MobiCore device */
        mcRet = mcCloseDevice(DEVICE_ID);
        if (MC_DRV_OK != mcRet)
        {
            LOG_E("TEE_Close(): mcCloseDevice returned: %d\n", mcRet);
            ret = TEE_ERR_MC_DEVICE;
        }

    } while (false);
}


/**
 * TEE_RSAGenerateKeyPair
 *
 * Generates RSA key pair and returns key pair data as wrapped object
 *
 * @param  keyType        [in]  Key pair type. RSA or RSACRT
 * @param  keyData        [in]  Pointer to the key data buffer
 * @param  keyDataLength  [in]  Key data buffer length
 * @param  keySize        [in]  Key size
 * @param  exponent       [in]  Exponent number
 * @param  soLen          [out] Key data secure object length
 */
teeResult_t TEE_RSAGenerateKeyPair(
    teeRsaKeyPairType_t keyType,
    uint8_t*            keyData,
    uint32_t            keyDataLength,
    uint32_t            keySize,
    uint32_t            exponent,
    uint32_t*           soLen
){
    teeResult_t         ret = TEE_ERR_NONE;
    tciMessage_ptr      pTci = NULL;
    mcSessionHandle_t   sessionHandle;
    mcBulkMap_t         mapInfo;
    mcResult_t          mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, keyData, keyDataLength, &mapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_RSA_GEN_KEY_PAIR;
        pTci->rsagenkey.type        = keyType;
        pTci->rsagenkey.keysize     = keySize;
        pTci->rsagenkey.keydata     = (uint32_t)mapInfo.sVirtualAddr;
        pTci->rsagenkey.keydatalen  = keyDataLength;
        pTci->rsagenkey.exponent    = exponent;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, keyData, &mapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_RSAGenerateKeyPair(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
            break;
        }

        /* Update secure object length */
        *soLen =  pTci->rsagenkey.solen;

    } while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}


/**
 * TEE_RSASign
 *
 * Signs given plain data and returns signature data
 *
 * @param  keyData          [in]  Pointer to key data buffer
 * @param  keyDataLength    [in]  Key data buffer length
 * @param  plainData        [in]  Pointer to plain data to be signed
 * @param  plainDataLength  [in]  Plain data length
 * @param  signatureData    [out] Pointer to signature data
 * @param  signatureDataLength  [out] Signature data length
 * @param  algorithm        [in]  RSA signature algorithm
 */
teeResult_t TEE_RSASign(
    const uint8_t*  keyData,
    const uint32_t  keyDataLength,
    const uint8_t*  plainData,
    const uint32_t  plainDataLength,
    uint8_t*        signatureData,
    uint32_t*       signatureDataLength,
    teeRsaSigAlg_t  algorithm
){
    teeResult_t        ret = TEE_ERR_NONE;
    tciMessage_ptr     pTci = NULL;
    mcSessionHandle_t  sessionHandle;
    mcBulkMap_t        keyMapInfo;
    mcBulkMap_t        plainMapInfo;
    mcBulkMap_t        signatureMapInfo;
    mcResult_t         mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, (void*)keyData, keyDataLength, &keyMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)plainData, plainDataLength, &plainMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)signatureData, *signatureDataLength, &signatureMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_RSA_SIGN;
        pTci->rsasign.keydata = (uint32_t)keyMapInfo.sVirtualAddr;
        pTci->rsasign.keydatalen = keyDataLength;

        pTci->rsasign.plaindata = (uint32_t)plainMapInfo.sVirtualAddr;
        pTci->rsasign.plaindatalen = plainDataLength;

        pTci->rsasign.signaturedata = (uint32_t)signatureMapInfo.sVirtualAddr;
        pTci->rsasign.signaturedatalen = *signatureDataLength;

        pTci->rsasign.algorithm = algorithm;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, (void*)keyData, &keyMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)plainData, &plainMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)signatureData, &signatureMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_RSASign(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
            break;
        }

        /* Retrieve signature data length */
        *signatureDataLength = pTci->rsasign.signaturedatalen;

    } while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}


/**
 * TEE_RSAVerify
 *
 * Verifies given data with RSA public key and return status
 *
 * @param  keyData          [in]  Pointer to key data buffer
 * @param  keyDataLength    [in]  Key data buffer length
 * @param  plainData        [in]  Pointer to plain data to be signed
 * @param  plainDataLength  [in]  Plain data length
 * @param  signatureData    [in]  Pointer to signed data
 * @param  signatureData    [in]  Plain  data length
 * @param  algorithm        [in]  RSA signature algorithm
 * @param  validity         [out] Signature validity
 */
teeResult_t TEE_RSAVerify(
    const uint8_t*  keyData,
    const uint32_t  keyDataLength,
    const uint8_t*  plainData,
    const uint32_t  plainDataLength,
    const uint8_t*  signatureData,
    const uint32_t  signatureDataLength,
    teeRsaSigAlg_t  algorithm,
    bool            *validity
){
    teeResult_t        ret = TEE_ERR_NONE;
    tciMessage_ptr     pTci = NULL;
    mcSessionHandle_t  sessionHandle;
    mcBulkMap_t        keyMapInfo;
    mcBulkMap_t        plainMapInfo;
    mcBulkMap_t        signatureMapInfo;
    mcResult_t         mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, (void*)keyData, keyDataLength, &keyMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)plainData, plainDataLength, &plainMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)signatureData, signatureDataLength, &signatureMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_RSA_VERIFY;
        pTci->rsaverify.keydata = (uint32_t)keyMapInfo.sVirtualAddr;
        pTci->rsaverify.keydatalen = keyDataLength;

        pTci->rsaverify.plaindata = (uint32_t)plainMapInfo.sVirtualAddr;
        pTci->rsaverify.plaindatalen = plainDataLength;

        pTci->rsaverify.signaturedata = (uint32_t)signatureMapInfo.sVirtualAddr;
        pTci->rsaverify.signaturedatalen = signatureDataLength;

        pTci->rsaverify.algorithm = algorithm;
        pTci->rsaverify.validity = false;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, (void*)keyData, &keyMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)plainData, &plainMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)signatureData, &signatureMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_RSAVerify(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
            break;
        }

        *validity =  pTci->rsaverify.validity;

    } while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}


/**
 * TEE_HMACKeyGenerate
 *
 * Generates random key for HMAC calculation and returns key data as wrapped object
 * (key is encrypted)
 *
 * @param  keyData        [out] Pointer to key data
 * @param  keyDataLength  [in]  Key data buffer length
 * @param  soLen          [out] Key data secure object length
 */
teeResult_t TEE_HMACKeyGenerate(
    uint8_t*  keyData,
    uint32_t  keyDataLength,
    uint32_t* soLen
){
    teeResult_t        ret = TEE_ERR_NONE;
    tciMessage_ptr     pTci = NULL;
    mcSessionHandle_t  sessionHandle;
    mcBulkMap_t        keyMapInfo;
    mcResult_t         mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, (void*)keyData, keyDataLength, &keyMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_HMAC_GEN_KEY;
        pTci->hmacgenkey.keydata = (uint32_t)keyMapInfo.sVirtualAddr;
        pTci->hmacgenkey.keydatalen = keyDataLength;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, (void*)keyData, &keyMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_RSAVerify(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
        }

        /* Update secure object length */
        *soLen =  pTci->hmacgenkey.solen;

    }while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}

/**
 * TEE_HMACSign
 *
 * Signs given plain data and returns HMAC signature data
 *
 * @param  keyData          [in]  Pointer to key data buffer
 * @param  keyDataLength    [in]  Key data buffer length
 * @param  plainData        [in]  Pointer to plain data to be signed
 * @param  plainDataLength  [in]  Plain data length
 * @param  signatureData    [out] Pointer to signature data
 * @param  signatureDataLength  [out] Signature data length
 * @param  digest           [in]  Digest type
 */
teeResult_t TEE_HMACSign(
    const uint8_t*  keyData,
    const uint32_t  keyDataLength,
    const uint8_t*  plainData,
    const uint32_t  plainDataLength,
    uint8_t*        signatureData,
    uint32_t*       signatureDataLength,
    teeDigest_t     digest
){
    teeResult_t        ret = TEE_ERR_NONE;
    tciMessage_ptr     pTci = NULL;
    mcSessionHandle_t  sessionHandle;
    mcBulkMap_t        keyMapInfo;
    mcBulkMap_t        plainMapInfo;
    mcBulkMap_t        signatureMapInfo;
    mcResult_t         mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, (void*)keyData, keyDataLength, &keyMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)plainData, plainDataLength, &plainMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)signatureData, *signatureDataLength, &signatureMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_HMAC_SIGN;
        pTci->hmacsign.keydata = (uint32_t)keyMapInfo.sVirtualAddr;
        pTci->hmacsign.keydatalen = keyDataLength;

        pTci->hmacsign.plaindata = (uint32_t)plainMapInfo.sVirtualAddr;
        pTci->hmacsign.plaindatalen = plainDataLength;

        pTci->hmacsign.signaturedata = (uint32_t)signatureMapInfo.sVirtualAddr;
        pTci->hmacsign.signaturedatalen = *signatureDataLength;

        pTci->hmacsign.digest = digest;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, (void*)keyData, &keyMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)plainData, &plainMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)signatureData, &signatureMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_HMACSign(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
            break;
        }

        /* Retrieve signature data length */
        *signatureDataLength = pTci->hmacsign.signaturedatalen;

    } while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}


/**
 * TEE_HMACVerify
 *
 * Verifies given data HMAC key data and return status
 *
 * @param  plainData        [in]  Pointer to plain data to be signed
 * @param  plainDataLength  [in]  Plain data length
 * @param  signatureData    [in]  Pointer to signed data
 * @param  signatureData    [in]  Plain  data length
 * @param  digest           [in]  Digest type
 * @param  validity         [out] Signature validity
 */
teeResult_t TEE_HMACVerify(
    const uint8_t*  keyData,
    const uint32_t  keyDataLength,
    const uint8_t*  plainData,
    const uint32_t  plainDataLength,
    const uint8_t*  signatureData,
    const uint32_t  signatureDataLength,
    teeDigest_t     digest,
    bool            *validity
){
    teeResult_t        ret = TEE_ERR_NONE;
    tciMessage_ptr     pTci = NULL;
    mcSessionHandle_t  sessionHandle;
    mcBulkMap_t        keyMapInfo;
    mcBulkMap_t        plainMapInfo;
    mcBulkMap_t        signatureMapInfo;
    mcResult_t         mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, (void*)keyData, keyDataLength, &keyMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)plainData, plainDataLength, &plainMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)signatureData, signatureDataLength, &signatureMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_HMAC_VERIFY;
        pTci->hmacverify.keydata = (uint32_t)keyMapInfo.sVirtualAddr;
        pTci->hmacverify.keydatalen = keyDataLength;

        pTci->hmacverify.plaindata = (uint32_t)plainMapInfo.sVirtualAddr;
        pTci->hmacverify.plaindatalen = plainDataLength;

        pTci->hmacverify.signaturedata = (uint32_t)signatureMapInfo.sVirtualAddr;
        pTci->hmacverify.signaturedatalen = signatureDataLength;

        pTci->hmacverify.digest = digest;
        pTci->hmacverify.validity = false;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, (void*)keyData, &keyMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)plainData, &plainMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)signatureData, &signatureMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_HMACVerify(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
            break;
        }

        *validity =  pTci->hmacverify.validity;

    } while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}


/**
 * TEE_KeyImport
 *
 * Imports key data and returns key data as secure object
 *
 * Key data needs to be in the following format
 *
 * RSA key data:
 * |--key metadata--|--public modulus--|--public exponent--|--private exponent--|
 *
 * RSA CRT key data:
 * |--key metadata--|--public modulus--|--public exponent--|--P--|--Q--|--DP--|--DQ--|--Qinv--|
 *
 * Where:
 * P:     secret prime factor
 * Q:     secret prime factor
 * DP:    d mod (p-1)
 * DQ:    d mod (q-1)
 * Qinv:  q^-1 mod p
 *
 * @param  keyData          [in]  Pointer to key data
 * @param  keyDataLength    [in]  Key data length
 * @param  soData           [out] Pointer to wrapped key data
 * @param  soDataLength     [out] Wrapped key data length
 */
teeResult_t TEE_KeyImport(
    const uint8_t*  keyData,
    const uint32_t  keyDataLength,
    uint8_t*        soData,
    uint32_t*       soDataLength
){
    teeResult_t         ret = TEE_ERR_NONE;
    tciMessage_ptr      pTci = NULL;
    mcSessionHandle_t   sessionHandle;
    mcBulkMap_t         keyMapInfo;
    mcBulkMap_t         soMapInfo;
    mcResult_t          mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, (void*)keyData, keyDataLength, &keyMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)soData, *soDataLength, &soMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_KEY_IMPORT;
        pTci->keyimport.keydata        = (uint32_t)keyMapInfo.sVirtualAddr;
        pTci->keyimport.keydatalen     = keyDataLength;
        pTci->keyimport.sodata         = (uint32_t)soMapInfo.sVirtualAddr;
        pTci->keyimport.sodatalen      = *soDataLength;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, (void*)keyData, &keyMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)soData, &soMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_KeyWrap(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
            break;
        }

        /* Update secure object length */
        *soDataLength =  pTci->keyimport.sodatalen;

    } while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}


/** * TEE_GetPubKey
 *
 * Retrieves public key daya (modulus and exponent) from wrapped key data
 *
 * @param  keyData          [in]  Pointer to key data
 * @param  keyDataLength    [in]  Key data length
 * @param  modulus          [out] Pointer to public key modulus data
 * @param  modulusLength    [out] Modulus data length
 * @param  exponent         [out] Pointer to public key exponent data
 * @param  exponentLength   [out] Exponent data length
 */
teeResult_t TEE_GetPubKey(
    const uint8_t*  keyData,
    const uint32_t  keyDataLength,
    uint8_t*        modulus,
    uint32_t*       modulusLength,
    uint8_t*        exponent,
    uint32_t*       exponentLength
){
    teeResult_t         ret = TEE_ERR_NONE;
    tciMessage_ptr      pTci = NULL;
    mcSessionHandle_t   sessionHandle;
    mcBulkMap_t         keyMapInfo;
    mcBulkMap_t         modMapInfo;
    mcBulkMap_t         expMapInfo;
    mcResult_t          mcRet;

    do {

        /* Open session to the trustlet */
        pTci = TEE_Open(&sessionHandle);
        if (!pTci) {
            ret = TEE_ERR_MEMORY;
            break;
        }

        /* Map memory to the secure world */
        mcRet = mcMap(&sessionHandle, (void*)keyData, keyDataLength, &keyMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)modulus, *modulusLength, &modMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcMap(&sessionHandle, (void*)exponent, *exponentLength, &expMapInfo);
        if (MC_DRV_OK != mcRet) {
            ret = TEE_ERR_MAP;
            break;
        }

        /* Update TCI buffer */
        pTci->command.header.commandId = CMD_ID_TEE_GET_PUB_KEY;
        pTci->getpubkey.keydata        = (uint32_t)keyMapInfo.sVirtualAddr;
        pTci->getpubkey.keydatalen     = keyDataLength;
        pTci->getpubkey.modulus        = (uint32_t)modMapInfo.sVirtualAddr;
        pTci->getpubkey.moduluslen     = *modulusLength;
        pTci->getpubkey.exponent       = (uint32_t)expMapInfo.sVirtualAddr;
        pTci->getpubkey.exponentlen    = *exponentLength;

        /* Notify the trustlet */
        mcRet = mcNotify(&sessionHandle);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Wait for response from the trustlet */
        if (MC_DRV_OK != mcWaitNotification(&sessionHandle, MC_INFINITE_TIMEOUT))
        {
            ret = TEE_ERR_NOTIFICATION;
            break;
        }

        /* Unmap memory */
        mcRet = mcUnmap(&sessionHandle, (void*)keyData, &keyMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)modulus, &modMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        mcRet = mcUnmap(&sessionHandle, (void*)exponent, &expMapInfo);
        if (MC_DRV_OK != mcRet)
        {
            ret = TEE_ERR_MAP;
            break;
        }

        if (RET_OK != pTci->response.header.returnCode)
        {
            LOG_E("TEE_GetPubKey(): TEE Keymaster trustlet returned: 0x%.8x\n",
                        pTci->response.header.returnCode);
            ret = TEE_ERR_FAIL;
            break;
        }

        /* Update  modulus and exponent lengths */
        *modulusLength =   pTci->getpubkey.moduluslen;
        *exponentLength =   pTci->getpubkey.exponentlen;

    } while (false);

    /* Close session to the trustlet */
    TEE_Close(sessionHandle);

    return ret;
}