| /* |
| * Copyright 2017 NXP |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * o Redistributions of source code must retain the above copyright notice, this list |
| * of conditions and the following disclaimer. |
| * |
| * o 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. |
| * |
| * o Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 "fsl_dcp.h" |
| |
| /******************************************************************************* |
| * Definitions |
| ******************************************************************************/ |
| |
| /*! 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 |
| ******************************************************************************/ |
| static dcp_context_t s_dcpContextSwitchingBuffer; |
| |
| /******************************************************************************* |
| * Code |
| ******************************************************************************/ |
| |
| static void dcp_reverse_and_copy(uint8_t *src, uint8_t *dest, size_t src_len) |
| { |
| for (int i = 0; i < src_len; i++) |
| { |
| dest[i] = src[src_len - 1 - i]; |
| } |
| } |
| |
| 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: |
| break; |
| } |
| |
| if (!((semaReg & DCP_CH0SEMA_VALUE_MASK) || (statReg & DCP_CH0STAT_ERROR_CODE_MASK))) |
| { |
| status = kStatus_Success; |
| } |
| |
| return status; |
| } |
| |
| static void dcp_clear_status(DCP_Type *base) |
| { |
| volatile uint32_t *dcpStatClrPtr = &base->STAT + 2u; |
| *dcpStatClrPtr = 0xFFu; |
| } |
| |
| static void dcp_clear_channel_status(DCP_Type *base, uint32_t mask) |
| { |
| volatile uint32_t *chStatClrPtr; |
| |
| if (mask & kDCP_Channel0) |
| { |
| chStatClrPtr = &base->CH0STAT + 2u; |
| *chStatClrPtr = 0xFFu; |
| } |
| if (mask & kDCP_Channel1) |
| { |
| chStatClrPtr = &base->CH1STAT + 2u; |
| *chStatClrPtr = 0xFFu; |
| } |
| if (mask & kDCP_Channel2) |
| { |
| chStatClrPtr = &base->CH2STAT + 2u; |
| *chStatClrPtr = 0xFFu; |
| } |
| if (mask & kDCP_Channel3) |
| { |
| chStatClrPtr = &base->CH3STAT + 2u; |
| *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) |
| { |
| keySize -= sizeof(uint32_t); |
| base->KEYDATA = ((uint32_t *)(uintptr_t)key)[i]; |
| i++; |
| } |
| return kStatus_Success; |
| } |
| |
| 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) != handle->channel) |
| { |
| /* disable global interrupt */ |
| uint32_t currPriMask = DisableGlobalIRQ(); |
| |
| /* re-check if our channel is still available */ |
| if ((base->STAT & (uint32_t)handle->channel) == 0) |
| { |
| 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: |
| break; |
| } |
| |
| if (cmdptr && chsema) |
| { |
| /* set out packet to DCP CMDPTR */ |
| *cmdptr = (uint32_t)dcpPacket; |
| |
| /* set the channel semaphore */ |
| *chsema = 1u; |
| } |
| |
| status = kStatus_Success; |
| } |
| |
| else |
| { |
| status = kStatus_DCP_Again; |
| } |
| /* global interrupt enable */ |
| EnableGlobalIRQ(currPriMask); |
| } |
| |
| else |
| { |
| return kStatus_DCP_Again; |
| } |
| |
| return status; |
| } |
| |
| 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) |
| { |
| 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) |
| { |
| 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; |
| } |
| |
| 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; |
| dcp_work_packet_t dcpWork = {0}; |
| |
| do |
| { |
| completionStatus = DCP_AES_EncryptEcbNonBlocking(base, handle, &dcpWork, plaintext, ciphertext, size); |
| } while (completionStatus == kStatus_DCP_Again); |
| |
| if (completionStatus != kStatus_Success) |
| { |
| return completionStatus; |
| } |
| |
| return DCP_WaitForChannelComplete(base, handle); |
| } |
| |
| 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) || (size % 16u)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| dcpPacket->control0 = 0x122u; /* 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 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 = (0xFFu << 8); /* KEY_SELECT = OTP_KEY */ |
| } |
| else if (handle->keySlot == kDCP_OtpUniqueKey) |
| { |
| dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 = (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 |= (1u << 11); /* PAYLOAD_KEY */ |
| } |
| else |
| { |
| dcpPacket->control1 = (handle->keySlot << 8); /* KEY_SELECT = keySlot */ |
| } |
| |
| return dcp_schedule_work(base, handle, dcpPacket); |
| } |
| |
| 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; |
| dcp_work_packet_t dcpWork = {0}; |
| |
| do |
| { |
| completionStatus = DCP_AES_DecryptEcbNonBlocking(base, handle, &dcpWork, ciphertext, plaintext, size); |
| } while (completionStatus == kStatus_DCP_Again); |
| |
| if (completionStatus != kStatus_Success) |
| { |
| return completionStatus; |
| } |
| |
| return DCP_WaitForChannelComplete(base, handle); |
| } |
| |
| 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) || (size % 16u)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| dcpPacket->control0 = 0x22u; /* 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 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 = (0xFFu << 8); /* KEY_SELECT = OTP_KEY */ |
| } |
| else if (handle->keySlot == kDCP_OtpUniqueKey) |
| { |
| dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 = (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 |= (1u << 11); /* PAYLOAD_KEY */ |
| } |
| else |
| { |
| dcpPacket->control1 = (handle->keySlot << 8); /* KEY_SELECT = keySlot */ |
| } |
| |
| return dcp_schedule_work(base, handle, dcpPacket); |
| } |
| |
| 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; |
| dcp_work_packet_t dcpWork = {0}; |
| |
| do |
| { |
| completionStatus = DCP_AES_EncryptCbcNonBlocking(base, handle, &dcpWork, plaintext, ciphertext, size, iv); |
| } while (completionStatus == kStatus_DCP_Again); |
| |
| if (completionStatus != kStatus_Success) |
| { |
| return completionStatus; |
| } |
| |
| return DCP_WaitForChannelComplete(base, handle); |
| } |
| |
| 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) || (size % 16u)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| dcpPacket->control0 = 0x322u; /* 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 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 |= (0xFFu << 8); /* KEY_SELECT = OTP_KEY */ |
| } |
| else if (handle->keySlot == kDCP_OtpUniqueKey) |
| { |
| dcpPacket->payloadPointer = (uint32_t)iv; |
| dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 |= (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[]. */ |
| dcp_memcpy(handle->iv, iv, 16); |
| dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; |
| dcpPacket->control0 |= (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); |
| } |
| |
| 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; |
| dcp_work_packet_t dcpWork = {0}; |
| |
| do |
| { |
| completionStatus = DCP_AES_DecryptCbcNonBlocking(base, handle, &dcpWork, ciphertext, plaintext, size, iv); |
| } while (completionStatus == kStatus_DCP_Again); |
| |
| if (completionStatus != kStatus_Success) |
| { |
| return completionStatus; |
| } |
| |
| return DCP_WaitForChannelComplete(base, handle); |
| } |
| |
| 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) || (size % 16u)) |
| { |
| return kStatus_InvalidArgument; |
| } |
| |
| dcpPacket->control0 = 0x222u; /* 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 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 |= (0xFFu << 8); /* OTP_KEY */ |
| } |
| else if (handle->keySlot == kDCP_OtpUniqueKey) |
| { |
| dcpPacket->payloadPointer = (uint32_t)iv; |
| dcpPacket->control0 |= (1u << 10); /* OTP_KEY */ |
| dcpPacket->control1 |= (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[]. */ |
| dcp_memcpy(handle->iv, iv, 16); |
| dcpPacket->payloadPointer = (uint32_t)&handle->keyWord[0]; |
| dcpPacket->control0 |= (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); |
| } |
| |
| 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. |
| */ |
| dcp_config_t userConfig = { |
| true, false, true, kDCP_chEnableAll, kDCP_chIntDisable, |
| }; |
| |
| *config = userConfig; |
| } |
| |
| 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, kDCP_Channel0 | kDCP_Channel1 | kDCP_Channel2 | 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; |
| } |
| |
| void DCP_Deinit(DCP_Type *base) |
| { |
| base->CTRL = 0xF0800000u; /* reset value */ |
| 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 */ |
| } |
| |
| 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) == handle->channel) |
| { |
| } |
| |
| if (dcp_get_channel_status(base, handle->channel) != kStatus_Success) |
| { |
| dcp_clear_status(base); |
| dcp_clear_channel_status(base, handle->channel); |
| return kStatus_Fail; |
| } |
| |
| 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 = 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 | kDCP_CONTROL0_ENABLE_HASH | kDCP_CONTROL0_DECR_SEMAPHOR; |
| if (ctxInternal->algo == kDCP_Sha256) |
| { |
| dcpPacket->control1 = kDCP_CONTROL1_HASH_SELECT_SHA256; |
| } |
| else if (ctxInternal->algo == kDCP_Sha1) |
| { |
| dcpPacket->control1 = kDCP_CONTROL1_HASH_SELECT_SHA1; |
| } |
| else if (ctxInternal->algo == kDCP_Crc32) |
| { |
| dcpPacket->control1 = 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; |
| |
| 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; |
| dcp_work_packet_t dcpWork = {0}; |
| |
| do |
| { |
| completionStatus = dcp_hash_update_non_blocking(base, ctxInternal, &dcpWork, msg, size); |
| } while (completionStatus == 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 > 0) |
| { |
| size_t toCopy = DCP_HASH_BLOCK_SIZE - ctxInternal->blksz; |
| 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 > 0) |
| { |
| 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 */ |
| 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 |= 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: |
| break; |
| } |
| if (srcAddr) |
| { |
| 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: |
| break; |
| } |
| if (destAddr) |
| { |
| dcp_memcpy(destAddr, ctxInternal->runningHash, sizeof(ctxInternal->runningHash)); |
| } |
| } |
| |
| 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; |
| /* 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 = (dcp_hash_ctx_internal_t *)ctx; |
| ctxInternal->algo = algo; |
| ctxInternal->blksz = 0u; |
| for (i = 0; i < sizeof(ctxInternal->blk.w) / sizeof(ctxInternal->blk.w[0]); i++) |
| { |
| ctxInternal->blk.w[0] = 0u; |
| } |
| ctxInternal->state = kDCP_StateHashInit; |
| ctxInternal->fullMessageSize = 0; |
| ctxInternal->handle = handle; |
| return status; |
| } |
| |
| 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; |
| |
| if (inputSize == 0) |
| { |
| return kStatus_Success; |
| } |
| |
| ctxInternal = (dcp_hash_ctx_internal_t *)ctx; |
| 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) |
| { |
| 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; |
| } |
| |
| 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; |
| |
| ctxInternal = (dcp_hash_ctx_internal_t *)ctx; |
| 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 = kDCP_OutLenSha256; |
| break; |
| case kDCP_Sha1: |
| outSize = kDCP_OutLenSha1; |
| break; |
| case kDCP_Crc32: |
| outSize = kDCP_OutLenCrc32; |
| break; |
| default: |
| break; |
| } |
| algOutSize = outSize; |
| |
| #if defined(DCP_HASH_CAVP_COMPATIBLE) |
| if (ctxInternal->fullMessageSize == 0) |
| { |
| switch (ctxInternal->algo) |
| { |
| case kDCP_Sha256: |
| dcp_memcpy(&output[0], &s_nullSha256, 32); |
| break; |
| case kDCP_Sha1: |
| dcp_memcpy(&output[0], &s_nullSha1, 20); |
| break; |
| default: |
| 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) |
| { |
| if (algOutSize < *outputSize) |
| { |
| *outputSize = algOutSize; |
| } |
| else |
| { |
| algOutSize = *outputSize; |
| } |
| } |
| |
| /* Reverse and copy result to output[] */ |
| dcp_reverse_and_copy((uint8_t *)ctxInternal->runningHash, &output[0], algOutSize); |
| |
| memset(ctx, 0, sizeof(dcp_hash_ctx_t)); |
| return status; |
| } |
| |
| 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; |
| 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; |
| } |