diff options
Diffstat (limited to 'bsps/arm/imxrt/mcux-sdk/drivers/dcp/fsl_dcp.c')
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/dcp/fsl_dcp.c | 1461 |
1 files changed, 1461 insertions, 0 deletions
diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/dcp/fsl_dcp.c b/bsps/arm/imxrt/mcux-sdk/drivers/dcp/fsl_dcp.c new file mode 100644 index 0000000000..a0b82bae1c --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/dcp/fsl_dcp.c @@ -0,0 +1,1461 @@ +/* + * Copyright 2017-2019 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_dcp.h" +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) +#include "fsl_cache.h" +#endif + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.dcp" +#endif + +/*! Compile time sizeof() check */ +#define BUILD_ASSURE(condition, msg) extern int msg[1 - 2 * (!(condition))] __attribute__((unused)) + +#define dcp_memcpy memcpy + +/*! Internal states of the HASH creation process */ +typedef enum _dcp_hash_algo_state +{ + kDCP_StateHashInit = 1u, /*!< Init state. */ + kDCP_StateHashUpdate, /*!< Update state. */ +} dcp_hash_algo_state_t; + +/*! multiple of 64-byte block represented as byte array of 32-bit words */ +typedef union _dcp_hash_block +{ + uint32_t w[DCP_HASH_BLOCK_SIZE / 4]; /*!< array of 32-bit words */ + uint8_t b[DCP_HASH_BLOCK_SIZE]; /*!< byte array */ +} dcp_hash_block_t; + +/*! internal dcp_hash context structure */ +typedef struct _dcp_hash_ctx_internal +{ + dcp_hash_block_t blk; /*!< memory buffer. only full blocks are written to DCP during hash updates */ + size_t blksz; /*!< number of valid bytes in memory buffer */ + dcp_hash_algo_t algo; /*!< selected algorithm from the set of supported algorithms */ + dcp_hash_algo_state_t state; /*!< finite machine state of the hash software process */ + uint32_t fullMessageSize; /*!< track message size */ + uint32_t ctrl0; /*!< HASH_INIT and HASH_TERM flags */ + uint32_t runningHash[9]; /*!< running hash. up to SHA-256 plus size, that is 36 bytes. */ + dcp_handle_t *handle; +} dcp_hash_ctx_internal_t; + +/*!< SHA-1/SHA-2 digest length in bytes */ +enum _dcp_hash_digest_len +{ + kDCP_OutLenSha1 = 20u, + kDCP_OutLenSha256 = 32u, + kDCP_OutLenCrc32 = 4u, +}; + +enum _dcp_work_packet_bit_definitions +{ + kDCP_CONTROL0_DECR_SEMAPHOR = 1u << 1, /* DECR_SEMAPHOR */ + kDCP_CONTROL0_ENABLE_HASH = 1u << 6, /* ENABLE_HASH */ + kDCP_CONTROL0_HASH_INIT = 1u << 12, /* HASH_INIT */ + kDCP_CONTROL0_HASH_TERM = 1u << 13, /* HASH_TERM */ + kDCP_CONTROL1_HASH_SELECT_SHA256 = 2u << 16, + kDCP_CONTROL1_HASH_SELECT_SHA1 = 0u << 16, + kDCP_CONTROL1_HASH_SELECT_CRC32 = 1u << 16, +}; + +/*! 64-byte block represented as byte array of 16 32-bit words */ +typedef union _dcp_sha_block +{ + uint32_t w[64 / 4]; /*!< array of 32-bit words */ + uint8_t b[64]; /*!< byte array */ +} dcp_sha_block_t; + +#if defined(DCP_HASH_CAVP_COMPATIBLE) +/* result of sha1 hash for message with zero size */ +static uint8_t s_nullSha1[] = {0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09}; +/* result of sha256 hash for message with zero size */ +static uint8_t s_nullSha256[] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, + 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, + 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; +#endif /* DCP_HASH_CAVP_COMPATIBLE */ + +/******************************************************************************* + * Variables + ******************************************************************************/ +AT_NONCACHEABLE_SECTION_INIT(static dcp_context_t s_dcpContextSwitchingBuffer); + +/******************************************************************************* + * Code + ******************************************************************************/ + +static void dcp_reverse_and_copy(uint8_t *src, uint8_t *dest, size_t src_len) +{ + for (uint32_t i = 0; i < src_len; i++) + { + dest[i] = src[src_len - 1U - i]; + } +} + +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) +static inline uint32_t *DCP_FindCacheLine(uint8_t *dcpWorkExt) +{ + while (0U != ((uint32_t)dcpWorkExt & ((uint32_t)FSL_FEATURE_L1DCACHE_LINESIZE_BYTE - 1U))) + { + dcpWorkExt++; + } + return (uint32_t *)(uint32_t)dcpWorkExt; +} +#endif + +static status_t dcp_get_channel_status(DCP_Type *base, dcp_channel_t channel) +{ + uint32_t statReg = 0; + uint32_t semaReg = 0; + status_t status = kStatus_Fail; + + switch (channel) + { + case kDCP_Channel0: + statReg = base->CH0STAT; + semaReg = base->CH0SEMA; + break; + + case kDCP_Channel1: + statReg = base->CH1STAT; + semaReg = base->CH1SEMA; + break; + + case kDCP_Channel2: + statReg = base->CH2STAT; + semaReg = base->CH2SEMA; + break; + + case kDCP_Channel3: + statReg = base->CH3STAT; + semaReg = base->CH3SEMA; + break; + + default: + /* All the cases have been listed above, the default clause should not be reached. */ + break; + } + + if (!((0U != (semaReg & DCP_CH0SEMA_VALUE_MASK)) || (0U != (statReg & DCP_CH0STAT_ERROR_CODE_MASK)))) + { + status = kStatus_Success; + } + + return status; +} + +static void dcp_clear_status(DCP_Type *base) +{ + volatile uint32_t *dcpStatClrPtr = (volatile uint32_t *)&base->STAT + 2u; + *dcpStatClrPtr = 0xFFu; + + while ((base->STAT & 0xffu) != 0U) + { + } +} + +static void dcp_clear_channel_status(DCP_Type *base, uint32_t mask) +{ + volatile uint32_t *chStatClrPtr; + + if (0U != (mask & (uint32_t)kDCP_Channel0)) + { + chStatClrPtr = &base->CH0STAT_CLR; + *chStatClrPtr = 0xFFu; + } + if (0U != (mask & (uint32_t)kDCP_Channel1)) + { + chStatClrPtr = &base->CH1STAT_CLR; + *chStatClrPtr = 0xFFu; + } + if (0U != (mask & (uint32_t)kDCP_Channel2)) + { + chStatClrPtr = &base->CH2STAT_CLR; + *chStatClrPtr = 0xFFu; + } + if (0U != (mask & (uint32_t)kDCP_Channel3)) + { + chStatClrPtr = &base->CH3STAT_CLR; + *chStatClrPtr = 0xFFu; + } +} + +static status_t dcp_aes_set_sram_based_key(DCP_Type *base, dcp_handle_t *handle, const uint8_t *key) +{ + base->KEY = DCP_KEY_INDEX(handle->keySlot) | DCP_KEY_SUBWORD(0); + /* move the key by 32-bit words */ + int i = 0; + size_t keySize = 16u; + while (keySize != 0U) + { + keySize -= sizeof(uint32_t); + base->KEYDATA = ((uint32_t *)(uintptr_t)key)[i]; + i++; + } + return kStatus_Success; +} + +/* Disable optimizations for GCC to prevent instruction reordering */ +#if defined(__GNUC__) +#pragma GCC push_options +#pragma GCC optimize("O0") +#endif +static status_t dcp_schedule_work(DCP_Type *base, dcp_handle_t *handle, dcp_work_packet_t *dcpPacket) +{ + status_t status; + + /* check if our channel is active */ + if ((base->STAT & (uint32_t)handle->channel) != (uint32_t)handle->channel) + { + /* disable global interrupt */ + uint32_t currPriMask = DisableGlobalIRQ(); + + /* re-check if our channel is still available */ + if ((base->STAT & (uint32_t)handle->channel) == 0U) + { + volatile uint32_t *cmdptr = NULL; + volatile uint32_t *chsema = NULL; + + switch (handle->channel) + { + case kDCP_Channel0: + cmdptr = &base->CH0CMDPTR; + chsema = &base->CH0SEMA; + break; + + case kDCP_Channel1: + cmdptr = &base->CH1CMDPTR; + chsema = &base->CH1SEMA; + break; + + case kDCP_Channel2: + cmdptr = &base->CH2CMDPTR; + chsema = &base->CH2SEMA; + break; + + case kDCP_Channel3: + cmdptr = &base->CH3CMDPTR; + chsema = &base->CH3SEMA; + break; + + default: + /* All the cases have been listed above, the default clause should not be reached. */ + break; + } + + if ((NULL != cmdptr) && (NULL != chsema)) + { + /* set out packet to DCP CMDPTR */ + *cmdptr = (uint32_t)dcpPacket; + +#if defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + /* Clean DCACHE before sending DCP packet to engine */ + DCACHE_CleanByRange((uint32_t)dcpPacket, sizeof(dcp_work_packet_t)); +#endif + /* Make sure that all data memory accesses are completed before starting of the job */ + __DSB(); + __ISB(); + + /* set the channel semaphore to start the job */ + *chsema = 1u; + } + + status = kStatus_Success; + } + + else + { + status = (int32_t)kStatus_DCP_Again; + } + /* global interrupt enable */ + EnableGlobalIRQ(currPriMask); + } + + else + { + return (int32_t)kStatus_DCP_Again; + } + + return status; +} +#if defined(__GNUC__) +#pragma GCC pop_options +#endif + +/*! + * brief Set AES key to dcp_handle_t struct and optionally to DCP. + * + * Sets the AES key for encryption/decryption with the dcp_handle_t structure. + * The dcp_handle_t input argument specifies keySlot. + * If the keySlot is kDCP_OtpKey, the function will check the OTP_KEY_READY bit and will return it's ready to use + * status. + * For other keySlot selections, the function will copy and hold the key in dcp_handle_t struct. + * If the keySlot is one of the four DCP SRAM-based keys (one of kDCP_KeySlot0, kDCP_KeySlot1, kDCP_KeySlot2, + * kDCP_KeySlot3), + * this function will also load the supplied key to the specified keySlot in DCP. + * + * param base DCP peripheral base address. + * param handle Handle used for the request. + * param key 0-mod-4 aligned pointer to AES key. + * param keySize AES key size in bytes. Shall equal 16. + * return status from set key operation + */ +status_t DCP_AES_SetKey(DCP_Type *base, dcp_handle_t *handle, const uint8_t *key, size_t keySize) +{ + status_t status = kStatus_Fail; + + if ((kDCP_OtpKey == handle->keySlot) || (kDCP_OtpUniqueKey == handle->keySlot)) + { + /* for AES OTP and unique key, check and return read from fuses status */ + if ((base->STAT & DCP_STAT_OTP_KEY_READY_MASK) == DCP_STAT_OTP_KEY_READY_MASK) + { + status = kStatus_Success; + } + } + else + { + /* only work with aligned key[] */ + if ((0x3U & (uintptr_t)key) != 0U) + { + return kStatus_InvalidArgument; + } + + /* keySize must be 16. */ + if (keySize != 16U) + { + return kStatus_InvalidArgument; + } + + /* move the key by 32-bit words */ + int i = 0; + while (keySize != 0U) + { + keySize -= sizeof(uint32_t); + handle->keyWord[i] = ((uint32_t *)(uintptr_t)key)[i]; + i++; + } + + if (kDCP_PayloadKey != handle->keySlot) + { + /* move the key by 32-bit words to DCP SRAM-based key storage */ + status = dcp_aes_set_sram_based_key(base, handle, key); + } + else + { + /* for PAYLOAD_KEY, just return Ok status now */ + status = kStatus_Success; + } + } + + return status; +} + +/*! + * brief Encrypts AES on one or multiple 128-bit block(s). + * + * Encrypts AES. + * The source plaintext and destination ciphertext can overlap in system memory. + * + * param base DCP peripheral base address + * param handle Handle used for this request. + * param plaintext Input plain text to encrypt + * param[out] ciphertext Output cipher text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * return Status from encrypt operation + */ +status_t DCP_AES_EncryptEcb( + DCP_Type *base, dcp_handle_t *handle, const uint8_t *plaintext, uint8_t *ciphertext, size_t size) +{ + status_t completionStatus = kStatus_Fail; + + /* Use extended DCACHE line size aligned structure */ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + dcp_work_packet_t *dcpWork; + uint8_t dcpWorkExt[sizeof(dcp_work_packet_t) + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE] = {0U}; + dcpWork = (dcp_work_packet_t *)(uint32_t)DCP_FindCacheLine(dcpWorkExt); +#else + dcp_work_packet_t dcpWorkPacket = {0}; + dcp_work_packet_t *dcpWork = &dcpWorkPacket; +#endif + + do + { + completionStatus = DCP_AES_EncryptEcbNonBlocking(base, handle, dcpWork, plaintext, ciphertext, size); + } while (completionStatus == (int32_t)kStatus_DCP_Again); + + if (completionStatus != kStatus_Success) + { + return completionStatus; + } + + return DCP_WaitForChannelComplete(base, handle); +} + +/*! + * brief Encrypts AES using the ECB block mode. + * + * Puts AES ECB encrypt work packet to DCP channel. + * + * param base DCP peripheral base address + * param handle Handle used for this request. + * param[out] dcpPacket Memory for the DCP work packet. + * param plaintext Input plain text to encrypt. + * param[out] ciphertext Output cipher text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * return kStatus_Success The work packet has been scheduled at DCP channel. + * return kStatus_DCP_Again The DCP channel is busy processing previous request. + */ +status_t DCP_AES_EncryptEcbNonBlocking(DCP_Type *base, + dcp_handle_t *handle, + dcp_work_packet_t *dcpPacket, + const uint8_t *plaintext, + uint8_t *ciphertext, + size_t size) +{ + /* Size must be 16-byte multiple */ + if ((size < 16u) || (0U != (size % 16u))) + { + return kStatus_InvalidArgument; + } + + dcpPacket->control0 = + 0x122u | (handle->swapConfig & 0xFC0000u); /* CIPHER_ENCRYPT | ENABLE_CIPHER | DECR_SEMAPHORE */ + dcpPacket->sourceBufferAddress = (uint32_t)plaintext; + dcpPacket->destinationBufferAddress = (uint32_t)ciphertext; + dcpPacket->bufferSize = (uint32_t)size; + + if (handle->keySlot == kDCP_OtpKey) + { + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 = ((uint32_t)0xFFu << 8); /* KEY_SELECT = OTP_KEY */ + } + else if (handle->keySlot == kDCP_OtpUniqueKey) + { + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 = ((uint32_t)0xFEu << 8); /* KEY_SELECT = UNIQUE_KEY */ + } + else if (handle->keySlot == kDCP_PayloadKey) + { + /* ECB does not have IV, so we can point payload directly to keyWord[] stored in handle. */ + dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; + dcpPacket->control0 |= ((uint32_t)1u << 11); /* PAYLOAD_KEY */ + } + else + { + dcpPacket->control1 = ((uint32_t)handle->keySlot << 8); /* KEY_SELECT = keySlot */ + } + + return dcp_schedule_work(base, handle, dcpPacket); +} + +/*! + * brief Decrypts AES on one or multiple 128-bit block(s). + * + * Decrypts AES. + * The source ciphertext and destination plaintext can overlap in system memory. + * + * param base DCP peripheral base address + * param handle Handle used for this request. + * param ciphertext Input plain text to encrypt + * param[out] plaintext Output cipher text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * return Status from decrypt operation + */ +status_t DCP_AES_DecryptEcb( + DCP_Type *base, dcp_handle_t *handle, const uint8_t *ciphertext, uint8_t *plaintext, size_t size) +{ + status_t completionStatus = kStatus_Fail; + + /* Use extended DCACHE line size aligned structure */ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + dcp_work_packet_t *dcpWork; + uint8_t dcpWorkExt[sizeof(dcp_work_packet_t) + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE] = {0U}; + dcpWork = (dcp_work_packet_t *)(uint32_t)DCP_FindCacheLine(dcpWorkExt); +#else + dcp_work_packet_t dcpWorkPacket = {0}; + dcp_work_packet_t *dcpWork = &dcpWorkPacket; +#endif + + do + { + completionStatus = DCP_AES_DecryptEcbNonBlocking(base, handle, dcpWork, ciphertext, plaintext, size); + } while (completionStatus == (int32_t)(kStatus_DCP_Again)); + + if (completionStatus != kStatus_Success) + { + return completionStatus; + } + + return DCP_WaitForChannelComplete(base, handle); +} + +/*! + * brief Decrypts AES using ECB block mode. + * + * Puts AES ECB decrypt dcpPacket to DCP input job ring. + * + * param base DCP peripheral base address + * param handle Handle used for this request. + * param[out] dcpPacket Memory for the DCP work packet. + * param ciphertext Input cipher text to decrypt + * param[out] plaintext Output plain text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * return kStatus_Success The work packet has been scheduled at DCP channel. + * return kStatus_DCP_Again The DCP channel is busy processing previous request. + */ +status_t DCP_AES_DecryptEcbNonBlocking(DCP_Type *base, + dcp_handle_t *handle, + dcp_work_packet_t *dcpPacket, + const uint8_t *ciphertext, + uint8_t *plaintext, + size_t size) +{ + /* Size must be 16-byte multiple */ + if ((size < 16u) || (0U != (size % 16u))) + { + return kStatus_InvalidArgument; + } + + dcpPacket->control0 = 0x22u | (handle->swapConfig & 0xFC0000u); /* ENABLE_CIPHER | DECR_SEMAPHORE */ + dcpPacket->sourceBufferAddress = (uint32_t)ciphertext; + dcpPacket->destinationBufferAddress = (uint32_t)plaintext; + dcpPacket->bufferSize = (uint32_t)size; + + if (handle->keySlot == kDCP_OtpKey) + { + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 = ((uint32_t)0xFFu << 8); /* KEY_SELECT = OTP_KEY */ + } + else if (handle->keySlot == kDCP_OtpUniqueKey) + { + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 = ((uint32_t)0xFEu << 8); /* KEY_SELECT = UNIQUE_KEY */ + } + else if (handle->keySlot == kDCP_PayloadKey) + { + /* ECB does not have IV, so we can point payload directly to keyWord[] stored in handle. */ + dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; + dcpPacket->control0 |= ((uint32_t)1u << 11); /* PAYLOAD_KEY */ + } + else + { + dcpPacket->control1 = ((uint32_t)handle->keySlot << 8); /* KEY_SELECT = keySlot */ + } + + return dcp_schedule_work(base, handle, dcpPacket); +} + +/*! + * brief Encrypts AES using CBC block mode. + * + * Encrypts AES using CBC block mode. + * The source plaintext and destination ciphertext can overlap in system memory. + * + * param base DCP peripheral base address + * param handle Handle used for this request. + * param plaintext Input plain text to encrypt + * param[out] ciphertext Output cipher text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * param iv Input initial vector to combine with the first input block. + * return Status from encrypt operation + */ +status_t DCP_AES_EncryptCbc(DCP_Type *base, + dcp_handle_t *handle, + const uint8_t *plaintext, + uint8_t *ciphertext, + size_t size, + const uint8_t iv[16]) +{ + status_t completionStatus = kStatus_Fail; + + /* Use extended DCACHE line size aligned structure */ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + dcp_work_packet_t *dcpWork; + uint8_t dcpWorkExt[sizeof(dcp_work_packet_t) + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE] = {0U}; + dcpWork = (dcp_work_packet_t *)(uint32_t)DCP_FindCacheLine(dcpWorkExt); +#else + dcp_work_packet_t dcpWorkPacket = {0}; + dcp_work_packet_t *dcpWork = &dcpWorkPacket; +#endif + + do + { + completionStatus = DCP_AES_EncryptCbcNonBlocking(base, handle, dcpWork, plaintext, ciphertext, size, iv); + } while (completionStatus == (int32_t)kStatus_DCP_Again); + + if (completionStatus != kStatus_Success) + { + return completionStatus; + } + + return DCP_WaitForChannelComplete(base, handle); +} + +/*! + * brief Encrypts AES using CBC block mode. + * + * Puts AES CBC encrypt dcpPacket to DCP input job ring. + * + * param base DCP peripheral base address + * param handle Handle used for this request. Specifies jobRing. + * param[out] dcpPacket Memory for the DCP work packet. + * param plaintext Input plain text to encrypt + * param[out] ciphertext Output cipher text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * param iv Input initial vector to combine with the first input block. + * return kStatus_Success The work packet has been scheduled at DCP channel. + * return kStatus_DCP_Again The DCP channel is busy processing previous request. + */ +status_t DCP_AES_EncryptCbcNonBlocking(DCP_Type *base, + dcp_handle_t *handle, + dcp_work_packet_t *dcpPacket, + const uint8_t *plaintext, + uint8_t *ciphertext, + size_t size, + const uint8_t *iv) +{ + /* Size must be 16-byte multiple */ + if ((size < 16u) || (0U != (size % 16u))) + { + return kStatus_InvalidArgument; + } + + dcpPacket->control0 = + 0x322u | (handle->swapConfig & 0xFC0000u); /* CIPHER_INIT | CIPHER_ENCRYPT | ENABLE_CIPHER | DECR_SEMAPHORE */ + dcpPacket->control1 = 0x10u; /* CBC */ + dcpPacket->sourceBufferAddress = (uint32_t)plaintext; + dcpPacket->destinationBufferAddress = (uint32_t)ciphertext; + dcpPacket->bufferSize = (uint32_t)size; + + if (handle->keySlot == kDCP_OtpKey) + { + dcpPacket->payloadPointer = (uint32_t)iv; + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 |= ((uint32_t)0xFFu << 8); /* KEY_SELECT = OTP_KEY */ + } + else if (handle->keySlot == kDCP_OtpUniqueKey) + { + dcpPacket->payloadPointer = (uint32_t)iv; + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 |= ((uint32_t)0xFEu << 8); /* KEY_SELECT = UNIQUE_KEY */ + } + else if (handle->keySlot == kDCP_PayloadKey) + { + /* In this case payload must contain key & iv in one array. */ + /* Copy iv into handle right behind the keyWord[] so we can point payload to keyWord[]. */ + (void)dcp_memcpy(handle->iv, (const uint32_t *)(uintptr_t)iv, 16); + dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; + dcpPacket->control0 |= ((uint32_t)1u << 11); /* PAYLOAD_KEY */ + } + else + { + dcpPacket->payloadPointer = (uint32_t)iv; + dcpPacket->control1 |= ((uint32_t)handle->keySlot << 8); /* KEY_SELECT = keySlot */ + } + + return dcp_schedule_work(base, handle, dcpPacket); +} + +/*! + * brief Decrypts AES using CBC block mode. + * + * Decrypts AES using CBC block mode. + * The source ciphertext and destination plaintext can overlap in system memory. + * + * param base DCP peripheral base address + * param handle Handle used for this request. + * param ciphertext Input cipher text to decrypt + * param[out] plaintext Output plain text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * param iv Input initial vector to combine with the first input block. + * return Status from decrypt operation + */ +status_t DCP_AES_DecryptCbc(DCP_Type *base, + dcp_handle_t *handle, + const uint8_t *ciphertext, + uint8_t *plaintext, + size_t size, + const uint8_t iv[16]) +{ + status_t completionStatus = kStatus_Fail; + + /* Use extended DCACHE line size aligned structure */ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + dcp_work_packet_t *dcpWork; + uint8_t dcpWorkExt[sizeof(dcp_work_packet_t) + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE] = {0U}; + dcpWork = (dcp_work_packet_t *)(uint32_t)DCP_FindCacheLine(dcpWorkExt); +#else + dcp_work_packet_t dcpWorkPacket = {0}; + dcp_work_packet_t *dcpWork = &dcpWorkPacket; +#endif + + do + { + completionStatus = DCP_AES_DecryptCbcNonBlocking(base, handle, dcpWork, ciphertext, plaintext, size, iv); + } while (completionStatus == (int32_t)kStatus_DCP_Again); + + if (completionStatus != (int32_t)kStatus_Success) + { + return completionStatus; + } + + return DCP_WaitForChannelComplete(base, handle); +} + +/*! + * brief Decrypts AES using CBC block mode. + * + * Puts AES CBC decrypt dcpPacket to DCP input job ring. + * + * param base DCP peripheral base address + * param handle Handle used for this request. Specifies jobRing. + * param[out] dcpPacket Memory for the DCP work packet. + * param ciphertext Input cipher text to decrypt + * param[out] plaintext Output plain text + * param size Size of input and output data in bytes. Must be multiple of 16 bytes. + * param iv Input initial vector to combine with the first input block. + * return kStatus_Success The work packet has been scheduled at DCP channel. + * return kStatus_DCP_Again The DCP channel is busy processing previous request. + */ +status_t DCP_AES_DecryptCbcNonBlocking(DCP_Type *base, + dcp_handle_t *handle, + dcp_work_packet_t *dcpPacket, + const uint8_t *ciphertext, + uint8_t *plaintext, + size_t size, + const uint8_t *iv) +{ + /* Size must be 16-byte multiple */ + if ((size < 16u) || (0U != (size % 16u))) + { + return kStatus_InvalidArgument; + } + + dcpPacket->control0 = 0x222u | (handle->swapConfig & 0xFC0000u); /* CIPHER_INIT | ENABLE_CIPHER | DECR_SEMAPHORE */ + dcpPacket->control1 = 0x10u; /* CBC */ + dcpPacket->sourceBufferAddress = (uint32_t)ciphertext; + dcpPacket->destinationBufferAddress = (uint32_t)plaintext; + dcpPacket->bufferSize = (uint32_t)size; + + if (handle->keySlot == kDCP_OtpKey) + { + dcpPacket->payloadPointer = (uint32_t)iv; + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 |= ((uint32_t)0xFFu << 8); /* OTP_KEY */ + } + else if (handle->keySlot == kDCP_OtpUniqueKey) + { + dcpPacket->payloadPointer = (uint32_t)iv; + dcpPacket->control0 |= ((uint32_t)1u << 10); /* OTP_KEY */ + dcpPacket->control1 |= ((uint32_t)0xFEu << 8); /* UNIQUE_KEY */ + } + else if (handle->keySlot == kDCP_PayloadKey) + { + /* in this case payload must contain KEY + IV together */ + /* copy iv into handle struct so we can point payload directly to keyWord[]. */ + (void)dcp_memcpy(handle->iv, (const uint32_t *)(uintptr_t)iv, 16); + dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; + dcpPacket->control0 |= ((uint32_t)1u << 11); /* PAYLOAD_KEY */ + } + else + { + dcpPacket->payloadPointer = (uint32_t)iv; + dcpPacket->control1 |= ((uint32_t)handle->keySlot << 8); /* KEY_SELECT */ + } + + return dcp_schedule_work(base, handle, dcpPacket); +} + +/*! + * brief Gets the default configuration structure. + * + * This function initializes the DCP configuration structure to a default value. The default + * values are as follows. + * dcpConfig->gatherResidualWrites = true; + * dcpConfig->enableContextCaching = true; + * dcpConfig->enableContextSwitching = true; + * dcpConfig->enableChannnel = kDCP_chEnableAll; + * dcpConfig->enableChannelInterrupt = kDCP_chIntDisable; + * + * param[out] config Pointer to configuration structure. + */ +void DCP_GetDefaultConfig(dcp_config_t *config) +{ + /* ENABLE_CONTEXT_CACHING is disabled by default as the DCP Hash driver uses + * dcp_hash_save_running_hash() and dcp_hash_restore_running_hash() to support + * Hash context switch (different messages interleaved) on the same channel. + */ + + /* Initializes the configure structure to zero. */ + (void)memset(config, 0, sizeof(*config)); + + dcp_config_t userConfig = { + true, false, true, (uint8_t)kDCP_chEnableAll, (uint8_t)kDCP_chIntDisable, + }; + + *config = userConfig; +} + +/*! + * brief Enables clock to and enables DCP + * + * Enable DCP clock and configure DCP. + * + * param base DCP base address + * param config Pointer to configuration structure. + */ +void DCP_Init(DCP_Type *base, const dcp_config_t *config) +{ +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + CLOCK_EnableClock(kCLOCK_Dcp); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + + base->CTRL = 0xF0800000u; /* reset value */ + base->CTRL = 0x30800000u; /* default value */ + + dcp_clear_status(base); + dcp_clear_channel_status( + base, (uint32_t)kDCP_Channel0 | (uint32_t)kDCP_Channel1 | (uint32_t)kDCP_Channel2 | (uint32_t)kDCP_Channel3); + + base->CTRL = DCP_CTRL_GATHER_RESIDUAL_WRITES(config->gatherResidualWrites) | + DCP_CTRL_ENABLE_CONTEXT_CACHING(config->enableContextCaching) | + DCP_CTRL_ENABLE_CONTEXT_SWITCHING(config->enableContextSwitching) | + DCP_CTRL_CHANNEL_INTERRUPT_ENABLE(config->enableChannelInterrupt); + + /* enable DCP channels */ + base->CHANNELCTRL = DCP_CHANNELCTRL_ENABLE_CHANNEL(config->enableChannel); + + /* use context switching buffer */ + base->CONTEXT = (uint32_t)&s_dcpContextSwitchingBuffer; +} + +/*! + * brief Disable DCP clock + * + * Reset DCP and Disable DCP clock. + * + * param base DCP base address + */ +void DCP_Deinit(DCP_Type *base) +{ + base->CTRL = 0xF0800000u; /* reset value */ + (void)memset(&s_dcpContextSwitchingBuffer, 0, sizeof(s_dcpContextSwitchingBuffer)); + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + CLOCK_DisableClock(kCLOCK_Dcp); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * brief Poll and wait on DCP channel. + * + * Polls the specified DCP channel until current it completes activity. + * + * param base DCP peripheral base address. + * param handle Specifies DCP channel. + * return kStatus_Success When data processing completes without error. + * return kStatus_Fail When error occurs. + */ +status_t DCP_WaitForChannelComplete(DCP_Type *base, dcp_handle_t *handle) +{ + /* wait if our channel is still active */ + while ((base->STAT & (uint32_t)handle->channel) == (uint32_t)handle->channel) + { + } + + if (dcp_get_channel_status(base, handle->channel) != kStatus_Success) + { + dcp_clear_status(base); + dcp_clear_channel_status(base, (uint32_t)handle->channel); + return kStatus_Fail; + } + + dcp_clear_status(base); + return kStatus_Success; +} + +/*! + * @brief Check validity of algoritm. + * + * This function checks the validity of input argument. + * + * @param algo Tested algorithm value. + * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise. + */ +static status_t dcp_hash_check_input_alg(dcp_hash_algo_t algo) +{ + if ((algo != kDCP_Sha256) && (algo != kDCP_Sha1) && (algo != kDCP_Crc32)) + { + return kStatus_InvalidArgument; + } + return kStatus_Success; +} + +/*! + * @brief Check validity of input arguments. + * + * This function checks the validity of input arguments. + * + * @param base DCP peripheral base address. + * @param ctx Memory buffer given by user application where the DCP_HASH_Init/DCP_HASH_Update/DCP_HASH_Finish store + * context. + * @param algo Tested algorithm value. + * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise. + */ +static status_t dcp_hash_check_input_args(DCP_Type *base, dcp_hash_ctx_t *ctx, dcp_hash_algo_t algo) +{ + /* Check validity of input algorithm */ + if (kStatus_Success != dcp_hash_check_input_alg(algo)) + { + return kStatus_InvalidArgument; + } + + if ((NULL == ctx) || (NULL == base)) + { + return kStatus_InvalidArgument; + } + + return kStatus_Success; +} + +/*! + * @brief Check validity of internal software context. + * + * This function checks if the internal context structure looks correct. + * + * @param ctxInternal Internal context. + * @param message Input message address. + * @return kStatus_Success if valid, kStatus_InvalidArgument otherwise. + */ +static status_t dcp_hash_check_context(dcp_hash_ctx_internal_t *ctxInternal, const uint8_t *message) +{ + if ((NULL == message) || (NULL == ctxInternal) || (kStatus_Success != dcp_hash_check_input_alg(ctxInternal->algo))) + { + return kStatus_InvalidArgument; + } + + return kStatus_Success; +} + +/*! + * @brief Initialize the SHA engine for new hash. + * + * This function sets kDCP_CONTROL0_HASH_INIT for control0 in work packet to start a new hash. + * + * @param base SHA peripheral base address. + * @param ctxInternal Internal context. + */ +static status_t dcp_hash_engine_init(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal) +{ + status_t status; + + status = kStatus_InvalidArgument; + + if ((kDCP_Sha256 == ctxInternal->algo) || (kDCP_Sha1 == ctxInternal->algo) || (kDCP_Crc32 == ctxInternal->algo)) + { + ctxInternal->ctrl0 = (uint32_t)kDCP_CONTROL0_HASH_INIT; + status = kStatus_Success; + } + + return status; +} + +static status_t dcp_hash_update_non_blocking( + DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal, dcp_work_packet_t *dcpPacket, const uint8_t *msg, size_t size) +{ + dcpPacket->control0 = ctxInternal->ctrl0 | (ctxInternal->handle->swapConfig & 0xFC0000u) | + (uint32_t)kDCP_CONTROL0_ENABLE_HASH | (uint32_t)kDCP_CONTROL0_DECR_SEMAPHOR; + if (ctxInternal->algo == kDCP_Sha256) + { + dcpPacket->control1 = (uint32_t)kDCP_CONTROL1_HASH_SELECT_SHA256; + } + else if (ctxInternal->algo == kDCP_Sha1) + { + dcpPacket->control1 = (uint32_t)kDCP_CONTROL1_HASH_SELECT_SHA1; + } + else if (ctxInternal->algo == kDCP_Crc32) + { + /* In CRC-32 case if size is zero, do not schedule other computing */ + if (size == 0U) + { +#if defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + /* Clear DCACHE memory before starting the engine */ + DCACHE_CleanByRange((uint32_t)ctxInternal, sizeof(dcp_hash_ctx_internal_t)); +#endif + /* Make sure that all data memory accesses are completed before starting of the job */ + __DSB(); + __ISB(); + return kStatus_Success; + } + dcpPacket->control1 = (uint32_t)kDCP_CONTROL1_HASH_SELECT_CRC32; + } + else + { + return kStatus_Fail; + } + dcpPacket->sourceBufferAddress = (uint32_t)msg; + dcpPacket->destinationBufferAddress = 0; + dcpPacket->bufferSize = size; + dcpPacket->payloadPointer = (uint32_t)ctxInternal->runningHash; + +#if defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + /* Clear DCACHE memory before starting the engine */ + DCACHE_CleanByRange((uint32_t)ctxInternal, sizeof(dcp_hash_ctx_internal_t)); +#endif + /* Make sure that all data memory accesses are completed before starting of the job */ + __DSB(); + __ISB(); + + return dcp_schedule_work(base, ctxInternal->handle, dcpPacket); +} + +static status_t dcp_hash_update(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal, const uint8_t *msg, size_t size) +{ + status_t completionStatus = kStatus_Fail; + + /* Use extended DCACHE line size aligned structure */ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + dcp_work_packet_t *dcpWork; + uint8_t dcpWorkExt[sizeof(dcp_work_packet_t) + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE] = {0U}; + dcpWork = (dcp_work_packet_t *)(uint32_t)DCP_FindCacheLine(dcpWorkExt); +#else + dcp_work_packet_t dcpWorkPacket = {0}; + dcp_work_packet_t *dcpWork = &dcpWorkPacket; +#endif + + do + { + completionStatus = dcp_hash_update_non_blocking(base, ctxInternal, dcpWork, msg, size); + } while (completionStatus == (int32_t)kStatus_DCP_Again); + + completionStatus = DCP_WaitForChannelComplete(base, ctxInternal->handle); + + ctxInternal->ctrl0 = 0; /* clear kDCP_CONTROL0_HASH_INIT and kDCP_CONTROL0_HASH_TERM flags */ + return (completionStatus); +} + +/*! + * @brief Adds message to current hash. + * + * This function merges the message to fill the internal buffer, empties the internal buffer if + * it becomes full, then process all remaining message data. + * + * + * @param base DCP peripheral base address. + * @param ctxInternal Internal context. + * @param message Input message. + * @param messageSize Size of input message in bytes. + * @return kStatus_Success. + */ +static status_t dcp_hash_process_message_data(DCP_Type *base, + dcp_hash_ctx_internal_t *ctxInternal, + const uint8_t *message, + size_t messageSize) +{ + status_t status = kStatus_Fail; + + /* if there is partially filled internal buffer, fill it to full block */ + if (ctxInternal->blksz > 0U) + { + size_t toCopy = DCP_HASH_BLOCK_SIZE - ctxInternal->blksz; + (void)dcp_memcpy(&ctxInternal->blk.b[ctxInternal->blksz], message, toCopy); + message += toCopy; + messageSize -= toCopy; + + /* process full internal block */ + status = dcp_hash_update(base, ctxInternal, &ctxInternal->blk.b[0], DCP_HASH_BLOCK_SIZE); + if (kStatus_Success != status) + { + return status; + } + } + + /* process all full blocks in message[] */ + uint32_t fullBlocksSize = ((messageSize >> 6) << 6); /* (X / 64) * 64 */ + if (fullBlocksSize > 0U) + { + status = dcp_hash_update(base, ctxInternal, message, fullBlocksSize); + if (kStatus_Success != status) + { + return status; + } + message += fullBlocksSize; + messageSize -= fullBlocksSize; + } + + /* copy last incomplete message bytes into internal block */ + (void)dcp_memcpy(&ctxInternal->blk.b[0], message, messageSize); + ctxInternal->blksz = messageSize; + + return status; +} + +/*! + * @brief Finalize the running hash to make digest. + * + * This function empties the internal buffer, adds padding bits, and generates final digest. + * + * @param base SHA peripheral base address. + * @param ctxInternal Internal context. + * @return kStatus_Success. + */ +static status_t dcp_hash_finalize(DCP_Type *base, dcp_hash_ctx_internal_t *ctxInternal) +{ + status_t status; + + ctxInternal->ctrl0 |= (uint32_t)kDCP_CONTROL0_HASH_TERM; + status = dcp_hash_update(base, ctxInternal, &ctxInternal->blk.b[0], ctxInternal->blksz); + + return status; +} + +static void dcp_hash_save_running_hash(dcp_hash_ctx_internal_t *ctxInternal) +{ + uint32_t *srcAddr = NULL; + + switch (ctxInternal->handle->channel) + { + case kDCP_Channel0: + srcAddr = &s_dcpContextSwitchingBuffer.x[43]; + break; + + case kDCP_Channel1: + srcAddr = &s_dcpContextSwitchingBuffer.x[30]; + break; + + case kDCP_Channel2: + srcAddr = &s_dcpContextSwitchingBuffer.x[17]; + break; + + case kDCP_Channel3: + srcAddr = &s_dcpContextSwitchingBuffer.x[4]; + break; + + default: + /* All the cases have been listed above, the default clause should not be reached. */ + break; + } + if (srcAddr != NULL) + { + DCACHE_InvalidateByRange((uint32_t)srcAddr, sizeof(ctxInternal->runningHash)); + (void)dcp_memcpy(ctxInternal->runningHash, srcAddr, sizeof(ctxInternal->runningHash)); + } +} + +static void dcp_hash_restore_running_hash(dcp_hash_ctx_internal_t *ctxInternal) +{ + uint32_t *destAddr = NULL; + + switch (ctxInternal->handle->channel) + { + case kDCP_Channel0: + destAddr = &s_dcpContextSwitchingBuffer.x[43]; + break; + + case kDCP_Channel1: + destAddr = &s_dcpContextSwitchingBuffer.x[30]; + break; + + case kDCP_Channel2: + destAddr = &s_dcpContextSwitchingBuffer.x[17]; + break; + + case kDCP_Channel3: + destAddr = &s_dcpContextSwitchingBuffer.x[4]; + break; + + default: + /* No valid channel */ + break; + } + if (destAddr != NULL) + { + (void)dcp_memcpy(destAddr, ctxInternal->runningHash, sizeof(ctxInternal->runningHash)); + } +} + +/*! + * brief Initialize HASH context + * + * This function initializes the HASH. + * + * param base DCP peripheral base address + * param handle Specifies the DCP channel used for hashing. + * param[out] ctx Output hash context + * param algo Underlaying algorithm to use for hash computation. + * return Status of initialization + */ +status_t DCP_HASH_Init(DCP_Type *base, dcp_handle_t *handle, dcp_hash_ctx_t *ctx, dcp_hash_algo_t algo) +{ + status_t status; + + dcp_hash_ctx_internal_t *ctxInternal; + /* Align structure on DCACHE line*/ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)((uint8_t *)ctx + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE); +#else + ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)ctx; +#endif + + /* compile time check for the correct structure size */ + BUILD_ASSURE(sizeof(dcp_hash_ctx_t) >= sizeof(dcp_hash_ctx_internal_t), dcp_hash_ctx_t_size); + uint32_t i; + + status = dcp_hash_check_input_args(base, ctx, algo); + if (status != kStatus_Success) + { + return status; + } + + /* set algorithm in context struct for later use */ + ctxInternal->algo = algo; + ctxInternal->blksz = 0u; + + const uint32_t j = sizeof(ctxInternal->blk.w) / sizeof(ctxInternal->blk.w[0]); + for (i = 0; i < j; i++) + { + ctxInternal->blk.w[i] = 0u; + } + ctxInternal->state = kDCP_StateHashInit; + ctxInternal->fullMessageSize = 0; + ctxInternal->handle = handle; + return status; +} + +/*! + * brief Add data to current HASH + * + * Add data to current HASH. This can be called repeatedly with an arbitrary amount of data to be + * hashed. The functions blocks. If it returns kStatus_Success, the running hash + * has been updated (DCP has processed the input data), so the memory at ref input pointer + * can be released back to system. The DCP context buffer is updated with the running hash + * and with all necessary information to support possible context switch. + * + * param base DCP peripheral base address + * param[in,out] ctx HASH context + * param input Input data + * param inputSize Size of input data in bytes + * return Status of the hash update operation + */ +status_t DCP_HASH_Update(DCP_Type *base, dcp_hash_ctx_t *ctx, const uint8_t *input, size_t inputSize) +{ + bool isUpdateState; + status_t status; + dcp_hash_ctx_internal_t *ctxInternal; + size_t blockSize; + + /* Align structure on DCACHE line*/ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)((uint8_t *)ctx + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE); +#else + ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)ctx; +#endif + + if (inputSize == 0U) + { + return kStatus_Success; + } + + status = dcp_hash_check_context(ctxInternal, input); + if (kStatus_Success != status) + { + return status; + } + + ctxInternal->fullMessageSize += inputSize; + blockSize = DCP_HASH_BLOCK_SIZE; + /* if we are still less than DCP_HASH_BLOCK_SIZE bytes, keep only in context */ + if ((ctxInternal->blksz + inputSize) <= blockSize) + { + (void)dcp_memcpy((&ctxInternal->blk.b[0]) + ctxInternal->blksz, input, inputSize); + ctxInternal->blksz += inputSize; + return status; + } + else + { + isUpdateState = ctxInternal->state == kDCP_StateHashUpdate; + if (!isUpdateState) + { + /* start NEW hash */ + status = dcp_hash_engine_init(base, ctxInternal); + if (status != kStatus_Success) + { + return status; + } + ctxInternal->state = kDCP_StateHashUpdate; + } + else + { + dcp_hash_restore_running_hash(ctxInternal); + } + } + + /* process input data */ + status = dcp_hash_process_message_data(base, ctxInternal, input, inputSize); + dcp_hash_save_running_hash(ctxInternal); + return status; +} + +/*! + * brief Finalize hashing + * + * Outputs the final hash (computed by DCP_HASH_Update()) and erases the context. + * + * param[in,out] ctx Input hash context + * param[out] output Output hash data + * param[in,out] outputSize Optional parameter (can be passed as NULL). On function entry, it specifies the size of + * output[] buffer. On function return, it stores the number of updated output bytes. + * return Status of the hash finish operation + */ +status_t DCP_HASH_Finish(DCP_Type *base, dcp_hash_ctx_t *ctx, uint8_t *output, size_t *outputSize) +{ + size_t algOutSize = 0; + status_t status; + dcp_hash_ctx_internal_t *ctxInternal; + + /* Align structure on DCACHE line*/ +#if defined(__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U) && defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)((uint8_t *)ctx + FSL_FEATURE_L1DCACHE_LINESIZE_BYTE); +#else + ctxInternal = (dcp_hash_ctx_internal_t *)(uint32_t)ctx; +#endif + + status = dcp_hash_check_context(ctxInternal, output); + + if (kStatus_Success != status) + { + return status; + } + + if (ctxInternal->state == kDCP_StateHashInit) + { + status = dcp_hash_engine_init(base, ctxInternal); + if (status != kStatus_Success) + { + return status; + } + } + else + { + dcp_hash_restore_running_hash(ctxInternal); + } + + size_t outSize = 0u; + + /* compute algorithm output length */ + switch (ctxInternal->algo) + { + case kDCP_Sha256: + outSize = (uint32_t)kDCP_OutLenSha256; + break; + case kDCP_Sha1: + outSize = (uint32_t)kDCP_OutLenSha1; + break; + case kDCP_Crc32: + outSize = (uint32_t)kDCP_OutLenCrc32; + break; + default: + /* All the cases have been listed above, the default clause should not be reached. */ + break; + } + algOutSize = outSize; + +#if defined(DCP_HASH_CAVP_COMPATIBLE) + if (ctxInternal->fullMessageSize == 0U) + { + switch (ctxInternal->algo) + { + case kDCP_Sha256: + (void)dcp_memcpy(&output[0], &s_nullSha256, 32); + break; + case kDCP_Sha1: + (void)dcp_memcpy(&output[0], &s_nullSha1, 20); + break; + default: + /* All the cases have been listed above, the default clause should not be reached. */ + break; + } + + return kStatus_Success; + } +#endif /* DCP_HASH_CAVP_COMPATIBLE */ + + /* flush message last incomplete block, if there is any, and add padding bits */ + status = dcp_hash_finalize(base, ctxInternal); + + if (outputSize != NULL) + { + if (algOutSize < *outputSize) + { + *outputSize = algOutSize; + } + else + { + algOutSize = *outputSize; + } + } + +#if defined(DCP_USE_DCACHE) && (DCP_USE_DCACHE == 1U) + DCACHE_InvalidateByRange((uint32_t)ctxInternal->runningHash, sizeof(ctxInternal->runningHash)); +#endif + /* Reverse and copy result to output[] */ + dcp_reverse_and_copy((uint8_t *)ctxInternal->runningHash, &output[0], algOutSize); + + (void)memset(ctx, 0, sizeof(dcp_hash_ctx_t)); + return status; +} + +/*! + * brief Create HASH on given data + * + * Perform the full SHA or CRC32 in one function call. The function is blocking. + * + * param base DCP peripheral base address + * param handle Handle used for the request. + * param algo Underlaying algorithm to use for hash computation. + * param input Input data + * param inputSize Size of input data in bytes + * param[out] output Output hash data + * param[out] outputSize Output parameter storing the size of the output hash in bytes + * return Status of the one call hash operation. + */ +status_t DCP_HASH(DCP_Type *base, + dcp_handle_t *handle, + dcp_hash_algo_t algo, + const uint8_t *input, + size_t inputSize, + uint8_t *output, + size_t *outputSize) +{ + dcp_hash_ctx_t hashCtx = {0}; + status_t status; + + status = DCP_HASH_Init(base, handle, &hashCtx, algo); + if (status != kStatus_Success) + { + return status; + } + + status = DCP_HASH_Update(base, &hashCtx, input, inputSize); + if (status != kStatus_Success) + { + return status; + } + + status = DCP_HASH_Finish(base, &hashCtx, output, outputSize); + + return status; +} |