knx/source/ti/devices/cc13x2_cc26x2/driverlib/aes.c
Nanosonde e51b65f8c2 Squashed 'examples/knx-cc1310/coresdk_cc13xx_cc26xx/' content from commit 0d78d32
git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx
git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5
2020-10-21 10:00:49 +02:00

373 lines
15 KiB
C

/******************************************************************************
* Filename: crypto.c
* Revised: 2019-01-25 13:11:50 +0100 (Fri, 25 Jan 2019)
* Revision: 54285
*
* Description: Driver for the aes functions of the crypto module
*
* Copyright (c) 2015 - 2017, Texas Instruments Incorporated
* All rights reserved.
*
* 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) Neither the name of the ORGANIZATION 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 "aes.h"
//*****************************************************************************
//
// Handle support for DriverLib in ROM:
// This section will undo prototype renaming made in the header file
//
//*****************************************************************************
#if !defined(DOXYGEN)
#undef AESStartDMAOperation
#define AESStartDMAOperation NOROM_AESStartDMAOperation
#undef AESSetInitializationVector
#define AESSetInitializationVector NOROM_AESSetInitializationVector
#undef AESWriteCCMInitializationVector
#define AESWriteCCMInitializationVector NOROM_AESWriteCCMInitializationVector
#undef AESReadTag
#define AESReadTag NOROM_AESReadTag
#undef AESVerifyTag
#define AESVerifyTag NOROM_AESVerifyTag
#undef AESWriteToKeyStore
#define AESWriteToKeyStore NOROM_AESWriteToKeyStore
#undef AESReadFromKeyStore
#define AESReadFromKeyStore NOROM_AESReadFromKeyStore
#undef AESWaitForIRQFlags
#define AESWaitForIRQFlags NOROM_AESWaitForIRQFlags
#undef AESConfigureCCMCtrl
#define AESConfigureCCMCtrl NOROM_AESConfigureCCMCtrl
#endif
//*****************************************************************************
//
// Load the initialization vector.
//
//*****************************************************************************
void AESSetInitializationVector(const uint32_t *initializationVector)
{
// Write initialization vector to the aes registers
HWREG(CRYPTO_BASE + CRYPTO_O_AESIV0) = initializationVector[0];
HWREG(CRYPTO_BASE + CRYPTO_O_AESIV1) = initializationVector[1];
HWREG(CRYPTO_BASE + CRYPTO_O_AESIV2) = initializationVector[2];
HWREG(CRYPTO_BASE + CRYPTO_O_AESIV3) = initializationVector[3];
}
//*****************************************************************************
//
// Start a crypto DMA operation.
//
//*****************************************************************************
void AESStartDMAOperation(const uint8_t *channel0Addr, uint32_t channel0Length, uint8_t *channel1Addr, uint32_t channel1Length)
{
if (channel0Length && channel0Addr) {
// We actually want to perform an operation. Clear any outstanding events.
HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = CRYPTO_IRQCLR_RESULT_AVAIL_M | CRYPTO_IRQEN_DMA_IN_DONE_M; // This might need AES_IRQEN_DMA_IN_DONE as well
while(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & (CRYPTO_IRQSTAT_DMA_IN_DONE_M | CRYPTO_IRQSTAT_RESULT_AVAIL_M));
// Configure the DMA controller - enable both DMA channels.
HWREGBITW(CRYPTO_BASE + CRYPTO_O_DMACH0CTL, CRYPTO_DMACH0CTL_EN_BITN) = 1;
// Base address of the payload data in ext. memory.
HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0EXTADDR) = (uint32_t)channel0Addr;
// Payload data length in bytes, equal to the cipher text length.
HWREG(CRYPTO_BASE + CRYPTO_O_DMACH0LEN) = channel0Length;
}
if (channel1Length && channel1Addr) {
// Enable DMA channel 1.
HWREGBITW(CRYPTO_BASE + CRYPTO_O_DMACH1CTL, CRYPTO_DMACH1CTL_EN_BITN) = 1;
// Base address of the output data buffer.
HWREG(CRYPTO_BASE + CRYPTO_O_DMACH1EXTADDR) = (uint32_t)channel1Addr;
// Output data length in bytes, equal to the cipher text length.
HWREG(CRYPTO_BASE + CRYPTO_O_DMACH1LEN) = channel1Length;
}
}
//*****************************************************************************
//
// Poll the IRQ status register and return.
//
//*****************************************************************************
uint32_t AESWaitForIRQFlags(uint32_t irqFlags)
{
uint32_t irqTrigger = 0;
// Wait for the DMA operation to complete. Add a delay to make sure we are
// not flooding the bus with requests too much.
do {
CPUdelay(1);
}
while(!(HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & irqFlags & (CRYPTO_IRQSTAT_DMA_IN_DONE_M |
CRYPTO_IRQSTAT_RESULT_AVAIL_M |
CRYPTO_IRQSTAT_DMA_BUS_ERR_M |
CRYPTO_IRQSTAT_KEY_ST_WR_ERR_M)));
// Save the IRQ trigger source
irqTrigger = HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & irqFlags;
// Clear IRQ flags
HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = irqTrigger;
return irqTrigger;
}
//*****************************************************************************
//
// Transfer a key from CM3 memory to a key store location.
//
//*****************************************************************************
uint32_t AESWriteToKeyStore(const uint8_t *aesKey, uint32_t aesKeyLength, uint32_t keyStoreArea)
{
// Check the arguments.
ASSERT((keyStoreArea == AES_KEY_AREA_0) ||
(keyStoreArea == AES_KEY_AREA_1) ||
(keyStoreArea == AES_KEY_AREA_2) ||
(keyStoreArea == AES_KEY_AREA_3) ||
(keyStoreArea == AES_KEY_AREA_4) ||
(keyStoreArea == AES_KEY_AREA_5) ||
(keyStoreArea == AES_KEY_AREA_6) ||
(keyStoreArea == AES_KEY_AREA_7));
ASSERT((aesKeyLength == AES_128_KEY_LENGTH_BYTES) ||
(aesKeyLength == AES_192_KEY_LENGTH_BYTES) ||
(aesKeyLength == AES_256_KEY_LENGTH_BYTES));
uint32_t keySize = 0;
switch (aesKeyLength) {
case AES_128_KEY_LENGTH_BYTES:
keySize = CRYPTO_KEYSIZE_SIZE_128_BIT;
break;
case AES_192_KEY_LENGTH_BYTES:
keySize = CRYPTO_KEYSIZE_SIZE_192_BIT;
break;
case AES_256_KEY_LENGTH_BYTES:
keySize = CRYPTO_KEYSIZE_SIZE_256_BIT;
break;
}
// Clear any previously written key at the keyLocation
AESInvalidateKey(keyStoreArea);
// Disable the external interrupt to stop the interrupt form propagating
// from the module to the System CPU.
IntDisable(INT_CRYPTO_RESULT_AVAIL_IRQ);
// Enable internal interrupts.
HWREG(CRYPTO_BASE + CRYPTO_O_IRQTYPE) = CRYPTO_IRQTYPE_LEVEL_M;
HWREG(CRYPTO_BASE + CRYPTO_O_IRQEN) = CRYPTO_IRQEN_DMA_IN_DONE_M | CRYPTO_IRQEN_RESULT_AVAIL_M;
// Configure master control module.
HWREG(CRYPTO_BASE + CRYPTO_O_ALGSEL) = CRYPTO_ALGSEL_KEY_STORE;
// Clear any outstanding events.
HWREG(CRYPTO_BASE + CRYPTO_O_IRQCLR) = (CRYPTO_IRQCLR_DMA_IN_DONE | CRYPTO_IRQCLR_RESULT_AVAIL);
// Configure the size of keys contained within the key store
// Do not write to the register if the correct key size is already set.
// Writing to this register causes all current keys to be invalidated.
uint32_t keyStoreKeySize = HWREG(CRYPTO_BASE + CRYPTO_O_KEYSIZE);
if (keySize != keyStoreKeySize) {
HWREG(CRYPTO_BASE + CRYPTO_O_KEYSIZE) = keySize;
}
// Enable key to write (e.g. Key 0).
HWREG(CRYPTO_BASE + CRYPTO_O_KEYWRITEAREA) = 1 << keyStoreArea;
// Total key length in bytes (16 for 1 x 128-bit key and 32 for 1 x 256-bit key).
AESStartDMAOperation(aesKey, aesKeyLength, 0, 0);
// Wait for the DMA operation to complete.
uint32_t irqTrigger = AESWaitForIRQFlags(CRYPTO_IRQCLR_RESULT_AVAIL | CRYPTO_IRQCLR_DMA_IN_DONE | CRYPTO_IRQSTAT_DMA_BUS_ERR | CRYPTO_IRQSTAT_KEY_ST_WR_ERR);
// Re-enable interrupts globally.
IntPendClear(INT_CRYPTO_RESULT_AVAIL_IRQ);
IntEnable(INT_CRYPTO_RESULT_AVAIL_IRQ);
// If we had a bus error or the key is not in the CRYPTO_O_KEYWRITTENAREA, return an error.
if ((irqTrigger & (CRYPTO_IRQSTAT_DMA_BUS_ERR_M | CRYPTO_IRQSTAT_KEY_ST_WR_ERR_M)) || !(HWREG(CRYPTO_BASE + CRYPTO_O_KEYWRITTENAREA) & (1 << keyStoreArea))) {
// There was an error in writing to the keyStore.
return AES_KEYSTORE_ERROR;
}
else {
return AES_SUCCESS;
}
}
//*****************************************************************************
//
// Transfer a key from the keyStoreArea to the internal buffer of the module.
//
//*****************************************************************************
uint32_t AESReadFromKeyStore(uint32_t keyStoreArea)
{
// Check the arguments.
ASSERT((keyStoreArea == AES_KEY_AREA_0) ||
(keyStoreArea == AES_KEY_AREA_1) ||
(keyStoreArea == AES_KEY_AREA_2) ||
(keyStoreArea == AES_KEY_AREA_3) ||
(keyStoreArea == AES_KEY_AREA_4) ||
(keyStoreArea == AES_KEY_AREA_5) ||
(keyStoreArea == AES_KEY_AREA_6) ||
(keyStoreArea == AES_KEY_AREA_7));
// Check if there is a valid key in the specified keyStoreArea
if (!(HWREG(CRYPTO_BASE + CRYPTO_O_KEYWRITTENAREA) & (1 << keyStoreArea))) {
return AES_KEYSTORE_AREA_INVALID;
}
// Enable keys to read (e.g. Key 0).
HWREG(CRYPTO_BASE + CRYPTO_O_KEYREADAREA) = keyStoreArea;
// Wait until key is loaded to the AES module.
// We cannot simply poll the IRQ status as only an error is communicated through
// the IRQ status and not the completion of the transfer.
do {
CPUdelay(1);
}
while((HWREG(CRYPTO_BASE + CRYPTO_O_KEYREADAREA) & CRYPTO_KEYREADAREA_BUSY_M));
// Check for keyStore read error.
if((HWREG(CRYPTO_BASE + CRYPTO_O_IRQSTAT) & CRYPTO_IRQSTAT_KEY_ST_RD_ERR_M)) {
return AES_KEYSTORE_ERROR;
}
else {
return AES_SUCCESS;
}
}
//*****************************************************************************
//
// Read the tag after a completed CCM, GCM, or CBC-MAC operation.
//
//*****************************************************************************
uint32_t AESReadTag(uint8_t *tag, uint32_t tagLength)
{
// The intermediate array is used instead of a caller-provided one
// to enable a simple API with no unintuitive alignment or size requirements.
// This is a trade-off of stack-depth vs ease-of-use that came out on the
// ease-of-use side.
uint32_t computedTag[AES_BLOCK_SIZE / sizeof(uint32_t)];
// Wait until the computed tag is ready.
while (!(HWREG(CRYPTO_BASE + CRYPTO_O_AESCTL) & CRYPTO_AESCTL_SAVED_CONTEXT_RDY_M));
// Read computed tag out from the HW registers
// Need to read out all 128 bits in four reads to correctly clear CRYPTO_AESCTL_SAVED_CONTEXT_RDY flag
computedTag[0] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT0);
computedTag[1] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT1);
computedTag[2] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT2);
computedTag[3] = HWREG(CRYPTO_BASE + CRYPTO_O_AESTAGOUT3);
memcpy(tag, computedTag, tagLength);
return AES_SUCCESS;
}
//*****************************************************************************
//
// Verify the provided tag against the computed tag after a completed CCM or
// GCM operation.
//
//*****************************************************************************
uint32_t AESVerifyTag(const uint8_t *tag, uint32_t tagLength)
{
uint32_t resultStatus;
// The intermediate array is allocated on the stack to ensure users do not
// point the tag they provide and the one computed at the same location.
// That would cause memcmp to compare an array against itself. We could add
// a check that verifies that the arrays are not the same. If we did that and
// modified AESReadTag to just copy all 128 bits into a provided array,
// we could save 16 bytes of stack space while making the API much more
// complicated.
uint8_t computedTag[AES_BLOCK_SIZE];
resultStatus = AESReadTag(computedTag, tagLength);
if (resultStatus != AES_SUCCESS) {
return resultStatus;
}
resultStatus = memcmp(computedTag, tag, tagLength);
if (resultStatus != 0) {
return AES_TAG_VERIFICATION_FAILED;
}
return AES_SUCCESS;
}
//*****************************************************************************
//
// Configure the AES module for CCM mode
//
//*****************************************************************************
void AESConfigureCCMCtrl(uint32_t nonceLength, uint32_t macLength, bool encrypt)
{
uint32_t ctrlVal = 0;
ctrlVal = ((15 - nonceLength - 1) << CRYPTO_AESCTL_CCM_L_S);
if ( macLength >= 2 ) {
ctrlVal |= ((( macLength - 2 ) >> 1 ) << CRYPTO_AESCTL_CCM_M_S );
}
ctrlVal |= CRYPTO_AESCTL_CCM |
CRYPTO_AESCTL_CTR |
CRYPTO_AESCTL_SAVE_CONTEXT |
CRYPTO_AESCTL_CTR_WIDTH_128_BIT;
ctrlVal |= encrypt ? CRYPTO_AESCTL_DIR : 0;
AESSetCtrl(ctrlVal);
}
//*****************************************************************************
//
// Configure an IV for CCM mode of operation
//
//*****************************************************************************
void AESWriteCCMInitializationVector(const uint8_t *nonce, uint32_t nonceLength)
{
union {
uint32_t word[4];
uint8_t byte[16];
} initializationVector = {{0}};
initializationVector.byte[0] = 15 - nonceLength - 1;
memcpy(&(initializationVector.byte[1]), nonce, nonceLength);
AESSetInitializationVector(initializationVector.word);
}