mirror of
https://github.com/thelsing/knx.git
synced 2025-10-12 11:15:54 +02:00
git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5
373 lines
15 KiB
C
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);
|
|
}
|