text: /* esp32_aes.c
   *
   * Copyright (C) 2006-2021 wolfSSL Inc.
   *
   * This file is part of wolfSSL.
   *
   * wolfSSL is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * wolfSSL is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
   */
  
  #include <string.h>
  #include <stdio.h>
  
  #ifdef HAVE_CONFIG_H
      #include <config.h>
  #endif
  #include <wolfssl/wolfcrypt/settings.h>
  
  #ifndef NO_AES
  
  #if defined(WOLFSSL_ESP32WROOM32_CRYPT) && \
      !defined(NO_WOLFSSL_ESP32WROOM32_CRYPT_AES)
  
  #include <wolfssl/wolfcrypt/aes.h>
  #include "wolfssl/wolfcrypt/port/Espressif/esp32-crypt.h"
  
  static const char* TAG = "wolf_hw_aes";
  /* mutex */
  static wolfSSL_Mutex aes_mutex;
  static int espaes_CryptHwMutexInit = 0;
  
  /*
  * lock hw engine.
  * this should be called before using engine.
  */
  static int esp_aes_hw_InUse()
  {
      int ret = 0;
  
      ESP_LOGV(TAG, "enter esp_aes_hw_InUse");
  
      if(espaes_CryptHwMutexInit == 0) {
          ret = esp_CryptHwMutexInit(&aes_mutex);
          if(ret == 0){
              espaes_CryptHwMutexInit = 1;
          } else {
              ESP_LOGE(TAG, "aes mutx initialization failed.");
              return -1;
          }
      }
      /* lock hardware */
      ret = esp_CryptHwMutexLock(&aes_mutex, portMAX_DELAY);
      if(ret != 0) {
          ESP_LOGE(TAG, "aes engine lock failed.");
          return -1;
      }
      /* Enable AES hardware */
      periph_module_enable(PERIPH_AES_MODULE);
  
      ESP_LOGV(TAG, "leave esp_aes_hw_InUse");
      return ret;
  }
  
  /*
  *   release hw engine
  */
  static void esp_aes_hw_Leave( void )
  {
      ESP_LOGV(TAG, "enter esp_aes_hw_Leave");
      /* Disable AES hardware */
      periph_module_disable(PERIPH_AES_MODULE);
  
      /* unlock */
      esp_CryptHwMutexUnLock(&aes_mutex);
  
      ESP_LOGV(TAG, "leave esp_aes_hw_Leave");
  }
  
  /*
   * set key to hardware key registers.
   */
  static void esp_aes_hw_Set_KeyMode(Aes *ctx, ESP32_AESPROCESS mode)
  {
      int i;
      word32 mode_ = 0;
  
      ESP_LOGV(TAG, "enter esp_aes_hw_Set_KeyMode");
  
      /* check mode */
      if(mode == ESP32_AES_UPDATEKEY_ENCRYPT) {
          mode_ = 0;
      } else if(mode == ESP32_AES_UPDATEKEY_DECRYPT){
          mode_ = 4;
      } else {
          ESP_LOGE(TAG, "unexpected error.");
          return;
      }
  
      /* update key */
      for(i=0;i<(ctx->keylen)/sizeof(word32);i++){
          DPORT_REG_WRITE(AES_KEY_BASE + (i*4), *(((word32*)ctx->key) + i));
      }
  
      /* mode
      *   0       AES-128 Encryption
      *   1       AES-192 Encryption
      *   2       AES-256 Encryption
      *   4       AES-128 Decryption
      *   5       AES-192 Decryption
      *   6       AES-256 Decryption
      */
      switch(ctx->keylen){
          case 24: mode_ += 1; break;
          case 32: mode_ += 2; break;
          default: break;
      }
  
      DPORT_REG_WRITE(AES_MODE_REG, mode_);
      ESP_LOGV(TAG, "leave esp_aes_hw_Setkey");
  }
  
  /*
   * Process a one block of AES
   */
  static void esp_aes_bk(const byte* in, byte* out)
  {
      const word32 *inwords = (const word32 *)in;
      word32 *outwords      = (word32 *)out;
  
      ESP_LOGV(TAG, "enter esp_aes_bk");
  
      /* copy text for encrypting/decrypting blocks */
      DPORT_REG_WRITE(AES_TEXT_BASE, inwords[0]);
      DPORT_REG_WRITE(AES_TEXT_BASE + 4, inwords[1]);
      DPORT_REG_WRITE(AES_TEXT_BASE + 8, inwords[2]);
      DPORT_REG_WRITE(AES_TEXT_BASE + 12, inwords[3]);
  
      /* start engine */
      DPORT_REG_WRITE(AES_START_REG, 1);
  
      /* wait until finishing the process */
      while(1) {
          if(DPORT_REG_READ(AES_IDLE_REG) == 1)
              break;
      }
  
      /* read-out blocks */
      esp_dport_access_read_buffer(outwords, AES_TEXT_BASE, 4);
      ESP_LOGV(TAG, "leave esp_aes_bk");
  }
  
  /*
  * wc_esp32AesEncrypt
  * @brief: a one block encrypt of the input block, into the output block
  * @param aes: a pointer of the AES object used to encrypt data
  * @param in : a pointer of the input buffer containing plain text to be encrypted
  * @param out: a pointer of the output buffer in which to store the cipher text of
  *             the encrypted message
  */
  int wc_esp32AesEncrypt(Aes *aes, const byte* in, byte* out)
  {
      ESP_LOGV(TAG, "enter wc_esp32AesEncrypt");
      /* lock the hw engine */
      esp_aes_hw_InUse();
      /* load the key into the register */
      esp_aes_hw_Set_KeyMode(aes, ESP32_AES_UPDATEKEY_ENCRYPT);
      /* process a one block of AES */
      esp_aes_bk(in, out);
      /* release hw */
      esp_aes_hw_Leave();
      return 0;
  }
  /*
  * wc_esp32AesDecrypt
  * @brief: a one block decrypt of the input block, into the output block
  * @param aes: a pointer of the AES object used to decrypt data
  * @param in : a pointer of the input buffer containing plain text to be decrypted
  * @param out: a pointer of the output buffer in which to store the cipher text of
  *             the decrypted message
  */
  int wc_esp32AesDecrypt(Aes *aes, const byte* in, byte* out)
  {
      ESP_LOGV(TAG, "enter wc_esp32AesDecrypt");
      /* lock the hw engine */
      esp_aes_hw_InUse();
      /* load the key into the register */
      esp_aes_hw_Set_KeyMode(aes, ESP32_AES_UPDATEKEY_DECRYPT);
      /* process a one block of AES */
      esp_aes_bk(in, out);
      /* release hw engine */
      esp_aes_hw_Leave();
      return 0;
  }
  /*
  * wc_esp32AesCbcEncrypt
  * @brief: Encrypts a plain text message from the input buffer, and places the
  *         resulting cipher text into the output buffer using cipher block chaining
  *         with AES.
  * @param aes: a pointer of the AES object used to encrypt data
  * @param out: a pointer of the output buffer in which to store the cipher text of
  *             the encrypted message
  * @param in : a pointer of the input buffer containing plain text to be encrypted
  * @param sz : size of input message
  */
  int wc_esp32AesCbcEncrypt(Aes* aes, byte* out, const byte* in, word32 sz)
  {
      int i;
      int offset = 0;
      word32 blocks = (sz / AES_BLOCK_SIZE);
      byte *iv;
      byte temp_block[AES_BLOCK_SIZE];
  
      ESP_LOGV(TAG, "enter wc_esp32AesCbcEncrypt");
  
      iv      = (byte*)aes->reg;
  
      esp_aes_hw_InUse();
  
      esp_aes_hw_Set_KeyMode(aes, ESP32_AES_UPDATEKEY_ENCRYPT);
  
      while (blocks--) {
            XMEMCPY(temp_block, in + offset, AES_BLOCK_SIZE);
  
           /* XOR block with IV for CBC */
           for (i = 0; i < AES_BLOCK_SIZE; i++)
               temp_block[i] ^= iv[i];
  
           esp_aes_bk(temp_block, (out + offset));
  
           offset += AES_BLOCK_SIZE;
  
           /* store IV for next block */
           XMEMCPY(iv, out + offset - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
      }
  
      esp_aes_hw_Leave();
      ESP_LOGV(TAG, "leave wc_esp32AesCbcEncrypt");
      return 0;
  }
  /*
  * wc_esp32AesCbcDecrypt
  * @brief: Encrypts a plain text message from the input buffer, and places the
  *         resulting cipher text into the output buffer using cipher block chaining
  *         with AES.
  * @param aes: a pointer of the AES object used to decrypt data
  * @param out: a pointer of the output buffer in which to store the cipher text of
  *             the decrypted message
  * @param in : a pointer of the input buffer containing plain text to be decrypted
  * @param sz : size of input message
  */
  int wc_esp32AesCbcDecrypt(Aes* aes, byte* out, const byte* in, word32 sz)
  {
      int i;
      int offset = 0;
      word32 blocks = (sz / AES_BLOCK_SIZE);
      byte* iv;
      byte temp_block[AES_BLOCK_SIZE];
  
      ESP_LOGV(TAG, "enter wc_esp32AesCbcDecrypt");
  
      iv      = (byte*)aes->reg;
  
      esp_aes_hw_InUse();
  
      esp_aes_hw_Set_KeyMode(aes, ESP32_AES_UPDATEKEY_DECRYPT);
  
      while (blocks--) {
          XMEMCPY(temp_block, in + offset, AES_BLOCK_SIZE);
  
          esp_aes_bk((in + offset), (out + offset));
  
          /* XOR block with IV for CBC */
          for (i = 0; i < AES_BLOCK_SIZE; i++)
              (out + offset)[i] ^= iv[i];
  
          /* store IV for next block */
          XMEMCPY(iv, temp_block, AES_BLOCK_SIZE);
  
          offset += AES_BLOCK_SIZE;
      }
  
      esp_aes_hw_Leave();
      ESP_LOGV(TAG, "leave wc_esp32AesCbcDecrypt");
      return 0;
  }
  
  #endif /* WOLFSSL_ESP32WROOM32_CRYPT */
  #endif /* NO_AES */
  