/****************************************************************************** * Filename: aes.c * * Description: Driver for the AES functions of the crypto module * * Copyright (c) 2015 - 2022, 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 #ifndef CRYPTO_SWRESET_SW_RESET /* This definition is missing in hw_crypto.h for CC26X0 and CC13X0 devices */ #define CRYPTO_SWRESET_SW_RESET 0x00000001 #endif #ifndef CRYPTO_DMASWRESET_SWRES /* This definition is missing in hw_crypto.h for CC26X0 and CC13X0 devices */ #define CRYPTO_DMASWRESET_SWRES 0x00000001 #endif #ifndef CRYPTO_DMASTAT_CH0_ACT /* This definition is missing in hw_crypto.h for CC26X0 and CC13X0 devices */ #define CRYPTO_DMASTAT_CH0_ACT 0x00000001 #endif #ifndef CRYPTO_DMASTAT_CH1_ACT /* This definition is missing in hw_crypto.h for CC26X0 and CC13X0 devices */ #define CRYPTO_DMASTAT_CH1_ACT 0x00000002 #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]; } //***************************************************************************** // // Read the IV for Authenticated Modes (CCM or GCM) after the tag has been read. // //***************************************************************************** void AESReadAuthenticationModeIV(uint32_t *iv) { /* Read the computed IV out from the hw registers */ iv[0] = HWREG(CRYPTO_BASE + CRYPTO_O_AESIV0); iv[1] = HWREG(CRYPTO_BASE + CRYPTO_O_AESIV1); iv[2] = HWREG(CRYPTO_BASE + CRYPTO_O_AESIV2); /* This read will clear the saved_context_ready bit * and allow the AES core to start the next operation. */ iv[3] = HWREG(CRYPTO_BASE + CRYPTO_O_AESIV3); } //***************************************************************************** // // Read the IV for Non-Authenticated Modes (CBC or CTR). // //***************************************************************************** void AESReadNonAuthenticationModeIV(uint32_t *iv) { /* Wait until the saved context is ready */ while(!(HWREG(CRYPTO_BASE + CRYPTO_O_AESCTL) & CRYPTO_AESCTL_SAVED_CONTEXT_RDY_M)); AESReadAuthenticationModeIV(iv); } //***************************************************************************** // // 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 CPU 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)); // This buffer must be declared at function scope to prevent LLVM compiler from optimizing out memcpy. uint8_t paddedKey[AES_256_KEY_LENGTH_BYTES] = {0}; 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; if (aesKeyLength == AES_192_KEY_LENGTH_BYTES) { // Writing a 192-bit key to the key store RAM must be done by writing // 256 bits of data with the 64 most significant bits set to zero. memcpy(paddedKey, aesKey, AES_192_KEY_LENGTH_BYTES); AESStartDMAOperation(paddedKey, AES_256_KEY_LENGTH_BYTES, 0, 0); } else { // 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); } //***************************************************************************** // // Write AES_KEY2 registers // //***************************************************************************** void AESWriteKey2(const uint32_t *key2) { HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY20) = key2[0]; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY21) = key2[1]; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY22) = key2[2]; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY23) = key2[3]; } //***************************************************************************** // // Write AES_KEY3 register // //***************************************************************************** void AESWriteKey3(const uint32_t *key3) { HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY30) = key3[0]; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY31) = key3[1]; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY32) = key3[2]; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY33) = key3[3]; } //***************************************************************************** // // Clear AES_DATA_IN registers // //***************************************************************************** void AESClearDataIn(void) { HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN0) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN1) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN2) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN3) = 0; } //***************************************************************************** // // Write AES_DATA_IN registers // //***************************************************************************** void AESWriteDataIn(const uint32_t *dataInBuffer) { HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN0) = dataInBuffer[0]; HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN1) = dataInBuffer[1]; HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN2) = dataInBuffer[2]; HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAIN3) = dataInBuffer[3]; } //***************************************************************************** // // Read AES_DATA_OUT registers // //***************************************************************************** void AESReadDataOut(uint32_t *dataOutBuffer) { dataOutBuffer[0] = HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAOUT0); dataOutBuffer[1] = HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAOUT1); dataOutBuffer[2] = HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAOUT2); dataOutBuffer[3] = HWREG(CRYPTO_BASE + CRYPTO_O_AESDATAOUT3); } //***************************************************************************** // // Clear AES_KEY2 registers // //***************************************************************************** void AESClearKey2(void) { HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY20) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY21) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY22) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY23) = 0; } //***************************************************************************** // // Clear AES_KEY3 registers // //***************************************************************************** void AESClearKey3(void) { HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY30) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY31) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY32) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_AESKEY33) = 0; } //***************************************************************************** // // Reset crypto engine // //***************************************************************************** void AESReset(void) { /* Soft reset routine per SafeXcel */ HWREG(CRYPTO_BASE + CRYPTO_O_SWRESET) = CRYPTO_SWRESET_SW_RESET; AESSetCtrl(0); AESSetDataLength(0); AESSetAuthLength(0); /* Only CC26X2, CC13X2, CC26X2F6, CC13X2F6, CC26X4, and CC13X4 devices have hash support */ HWREG(CRYPTO_BASE + CRYPTO_O_HASHMODE) = 0; HWREG(CRYPTO_BASE + CRYPTO_O_HASHINLENL) = 0; /* CRYPTO_O_HASHINLENH is automatically set to 0 by HW */ } //***************************************************************************** // // Reset crypto DMA // //***************************************************************************** void AESDMAReset(void) { /* Reset DMA */ HWREG(CRYPTO_BASE + CRYPTO_O_DMASWRESET) = CRYPTO_DMASWRESET_SWRES; /* Wait for DMA channels to be inactive */ while (HWREG(CRYPTO_BASE + CRYPTO_O_DMASTAT) & (CRYPTO_DMASTAT_CH0_ACT | CRYPTO_DMASTAT_CH1_ACT)); }