/* * Copyright (c) 2015-2016, Nuvoton Technology Corporation * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. * * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf */ #include "mbedtls/aes.h" #if defined(MBEDTLS_AES_C) #if defined(MBEDTLS_AES_ALT) #include <string.h> #include <stdbool.h> #include "M480.h" #include "mbed_toolchain.h" #include "mbed_assert.h" #include "mbed_error.h" #include "nu_bitutil.h" #include "crypto-misc.h" /* Implementation that should never be optimized out by the compiler */ static void mbedtls_zeroize( void *v, size_t n ) { volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0; } /* AES DMA compatible backup buffer if user buffer doesn't meet requirements * * AES DMA buffer location requires to be: * (1) Word-aligned * (2) Located in 0x2xxxxxxx region. Check linker files to ensure global variables are placed in this region. * * AES DMA buffer size MAX_DMA_CHAIN_SIZE must be a multiple of 16-byte block size. * Its value is estimated to trade memory footprint off against performance. * */ #define MAX_DMA_CHAIN_SIZE (16*6) MBED_ALIGN(4) static uint8_t au8OutputData[MAX_DMA_CHAIN_SIZE]; MBED_ALIGN(4) static uint8_t au8InputData[MAX_DMA_CHAIN_SIZE]; void mbedtls_aes_init( mbedtls_aes_context *ctx ) { memset( ctx, 0, sizeof( mbedtls_aes_context ) ); } void mbedtls_aes_free( mbedtls_aes_context *ctx ) { if( ctx == NULL ) return; mbedtls_zeroize( ctx, sizeof( mbedtls_aes_context ) ); } /* * AES key schedule (encryption) */ int mbedtls_aes_setkey_enc( mbedtls_aes_context *ctx, const unsigned char *key, unsigned int keybits ) { unsigned int i; switch( keybits ) { case 128: ctx->keySize = AES_KEY_SIZE_128; break; case 192: ctx->keySize = AES_KEY_SIZE_192; break; case 256: ctx->keySize = AES_KEY_SIZE_256; break; default : return( MBEDTLS_ERR_AES_INVALID_KEY_LENGTH ); } /* Fetch key byte data in big-endian */ for( i = 0; i < ( keybits >> 5 ); i++ ) { ctx->keys[i] = nu_get32_be(key + i * 4); } return( 0 ); } /* * AES key schedule (decryption) */ int mbedtls_aes_setkey_dec( mbedtls_aes_context *ctx, const unsigned char *key, unsigned int keybits ) { int ret; /* Also checks keybits */ if( ( ret = mbedtls_aes_setkey_enc( ctx, key, keybits ) ) != 0 ) goto exit; exit: return( ret ); } /* Do AES encrypt/decrypt with H/W accelerator * * NOTE: As input/output buffer doesn't follow constraint of DMA buffer, static allocated * DMA compatible buffer is used for DMA instead and this needs extra copy. * * NOTE: dataSize requires to be: * 1) Multiple of block size 16 * 2) <= MAX_DMA_CHAIN_SIZE */ static void __nvt_aes_crypt( mbedtls_aes_context *ctx, const unsigned char *input, unsigned char *output, size_t dataSize) { const unsigned char* pIn; unsigned char* pOut; MBED_ASSERT((dataSize % 16 == 0) && (dataSize <= MAX_DMA_CHAIN_SIZE)); /* AES DMA buffer has the following requirements: * (1) Word-aligned buffer base address * (2) 16-byte aligned buffer size * (3) Located in 0x20000000-0x2FFFFFFF region */ if ((! crypto_dma_buff_compat(au8OutputData, MAX_DMA_CHAIN_SIZE, 16)) || (! crypto_dma_buff_compat(au8InputData, MAX_DMA_CHAIN_SIZE, 16))) { error("Buffer for AES alter. DMA requires to be word-aligned and located in 0x20000000-0x2FFFFFFF region."); } /* Acquire ownership of AES H/W */ crypto_aes_acquire(); /* Init crypto module */ crypto_init(); /* Enable AES interrupt */ AES_ENABLE_INT(CRPT); /* We support multiple contexts with context save & restore and so needs just one * H/W channel. Always use H/W channel #0. */ /* AES_IN_OUT_SWAP: Let H/W know both input/output data are arranged in little-endian */ AES_Open(CRPT, 0, ctx->encDec, ctx->opMode, ctx->keySize, AES_IN_OUT_SWAP); AES_SetInitVect(CRPT, 0, ctx->iv); AES_SetKey(CRPT, 0, ctx->keys, ctx->keySize); /* AES DMA buffer requirements same as above */ if (! crypto_dma_buff_compat(input, dataSize, 16)) { memcpy(au8InputData, input, dataSize); pIn = au8InputData; } else { pIn = input; } /* AES DMA buffer requirements same as above */ if (! crypto_dma_buff_compat(output, dataSize, 16)) { pOut = au8OutputData; } else { pOut = output; } /* Even though AES H/W has limited support for overlapped DMA input/output buffers, * we still seek to one backup buffer to make them non-overlapped for simplicity. */ if (crypto_dma_buffs_overlap(pIn, dataSize, pOut, dataSize)) { memcpy(au8InputData, input, dataSize); pIn = au8InputData; } MBED_ASSERT(! crypto_dma_buffs_overlap(pIn, dataSize, pOut, dataSize)); AES_SetDMATransfer(CRPT, 0, (uint32_t)pIn, (uint32_t)pOut, dataSize); crypto_aes_prestart(); AES_Start(CRPT, 0, CRYPTO_DMA_ONE_SHOT); crypto_aes_wait(); if( pOut != output ) { memcpy(output, au8OutputData, dataSize); } /* Save IV for next block */ ctx->iv[0] = CRPT->AES_FDBCK[0]; ctx->iv[1] = CRPT->AES_FDBCK[1]; ctx->iv[2] = CRPT->AES_FDBCK[2]; ctx->iv[3] = CRPT->AES_FDBCK[3]; /* Disable AES interrupt */ AES_DISABLE_INT(CRPT); /* Uninit crypto module */ crypto_uninit(); /* Release ownership of AES H/W */ crypto_aes_release(); } /* * AES-ECB block encryption */ void mbedtls_aes_encrypt( mbedtls_aes_context *ctx, const unsigned char input[16], unsigned char output[16] ) { ctx->encDec = 1; __nvt_aes_crypt(ctx, input, output, 16); } /* * AES-ECB block decryption */ void mbedtls_aes_decrypt( mbedtls_aes_context *ctx, const unsigned char input[16], unsigned char output[16] ) { ctx->encDec = 0; __nvt_aes_crypt(ctx, input, output, 16); } /* * AES-ECB block encryption/decryption */ int mbedtls_aes_crypt_ecb( mbedtls_aes_context *ctx, int mode, const unsigned char input[16], unsigned char output[16] ) { ctx->opMode = AES_MODE_ECB; if( mode == MBEDTLS_AES_ENCRYPT ) mbedtls_aes_encrypt( ctx, input, output ); else mbedtls_aes_decrypt( ctx, input, output ); return( 0 ); } #if defined(MBEDTLS_CIPHER_MODE_CBC) /* * AES-CBC buffer encryption/decryption */ int mbedtls_aes_crypt_cbc( mbedtls_aes_context *ctx, int mode, size_t len, unsigned char iv[16], const unsigned char *input, unsigned char *output ) { int length = len; int blockChainLen; if( length % 16 ) return( MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH ); ctx->opMode = AES_MODE_CBC; /* Fetch IV byte data in big-endian */ ctx->iv[0] = nu_get32_be(iv); ctx->iv[1] = nu_get32_be(iv + 4); ctx->iv[2] = nu_get32_be(iv + 8); ctx->iv[3] = nu_get32_be(iv + 12); if( mode == MBEDTLS_AES_ENCRYPT ) { ctx->encDec = 1; } else { ctx->encDec = 0; } while( length > 0 ) { blockChainLen = (length > MAX_DMA_CHAIN_SIZE) ? MAX_DMA_CHAIN_SIZE : length; __nvt_aes_crypt(ctx, input, output, blockChainLen); length -= blockChainLen; input += blockChainLen; output += blockChainLen; } /* Save IV for next block cipher */ nu_set32_be(iv, ctx->iv[0]); nu_set32_be(iv + 4, ctx->iv[1]); nu_set32_be(iv + 8, ctx->iv[2]); nu_set32_be(iv + 12, ctx->iv[3]); return( 0 ); } #endif /* MBEDTLS_CIPHER_MODE_CBC */ #if defined(MBEDTLS_CIPHER_MODE_CFB) int mbedtls_aes_crypt_cfb128( mbedtls_aes_context *ctx, int mode, size_t length, size_t *iv_off, unsigned char iv[16], const unsigned char *input, unsigned char *output ) { int c; size_t n = *iv_off; /* First incomplete block */ if (n % 16) { while (n && length) { if (mode == MBEDTLS_AES_DECRYPT) { c = *input++; *output++ = (unsigned char)( c ^ iv[n] ); iv[n] = (unsigned char) c; } else { iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); } n = ( n + 1 ) & 0x0F; length --; } } /* Middle complete block(s) */ size_t block_chain_len = length - (length % 16); if (block_chain_len) { ctx->opMode = AES_MODE_CFB; if (mode == MBEDTLS_AES_DECRYPT) { ctx->encDec = 0; } else { ctx->encDec = 1; } /* Fetch IV byte data in big-endian */ ctx->iv[0] = nu_get32_be(iv); ctx->iv[1] = nu_get32_be(iv + 4); ctx->iv[2] = nu_get32_be(iv + 8); ctx->iv[3] = nu_get32_be(iv + 12); while (block_chain_len) { size_t block_chain_len2 = (block_chain_len > MAX_DMA_CHAIN_SIZE) ? MAX_DMA_CHAIN_SIZE : block_chain_len; __nvt_aes_crypt(ctx, input, output, block_chain_len2); input += block_chain_len2; output += block_chain_len2; length -= block_chain_len2; block_chain_len -= block_chain_len2; } /* NOTE: Buffers input/output could overlap. See ctx->iv rather than input/output * for iv of next block cipher. */ /* Fetch IV byte data in big-endian */ nu_set32_be(iv, ctx->iv[0]); nu_set32_be(iv + 4, ctx->iv[1]); nu_set32_be(iv + 8, ctx->iv[2]); nu_set32_be(iv + 12, ctx->iv[3]); } /* Last incomplete block */ if (length) { mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv ); while (length --) { if (mode == MBEDTLS_AES_DECRYPT) { c = *input++; *output++ = (unsigned char)( c ^ iv[n] ); iv[n] = (unsigned char) c; } else { iv[n] = *output++ = (unsigned char)( iv[n] ^ *input++ ); } n = ( n + 1 ) & 0x0F; } } *iv_off = n; return( 0 ); } /* * AES-CFB8 buffer encryption/decryption */ int mbedtls_aes_crypt_cfb8( mbedtls_aes_context *ctx, int mode, size_t length, unsigned char iv[16], const unsigned char *input, unsigned char *output ) { unsigned char c; unsigned char ov[17]; while( length-- ) { memcpy( ov, iv, 16 ); mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, iv, iv ); if( mode == MBEDTLS_AES_DECRYPT ) ov[16] = *input; c = *output++ = (unsigned char)( iv[0] ^ *input++ ); if( mode == MBEDTLS_AES_ENCRYPT ) ov[16] = c; memcpy( iv, ov + 1, 16 ); } return( 0 ); } #endif /*MBEDTLS_CIPHER_MODE_CFB */ #if defined(MBEDTLS_CIPHER_MODE_CTR) /* * AES-CTR buffer encryption/decryption */ int mbedtls_aes_crypt_ctr( mbedtls_aes_context *ctx, size_t length, size_t *nc_off, unsigned char nonce_counter[16], unsigned char stream_block[16], const unsigned char *input, unsigned char *output ) { int c, i; size_t n = *nc_off; while( length-- ) { if( n == 0 ) { mbedtls_aes_crypt_ecb( ctx, MBEDTLS_AES_ENCRYPT, nonce_counter, stream_block ); for( i = 16; i > 0; i-- ) if( ++nonce_counter[i - 1] != 0 ) break; } c = *input++; *output++ = (unsigned char)( c ^ stream_block[n] ); n = ( n + 1 ) & 0x0F; } *nc_off = n; return( 0 ); } #endif /* MBEDTLS_CIPHER_MODE_CTR */ #endif /* MBEDTLS_AES_ALT */ #endif /* MBEDTLS_AES_C */