text: /* pic32mz-crypt.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
   */
  
  
  #ifdef HAVE_CONFIG_H
      #include <config.h>
  #endif
  
  #include <wolfssl/wolfcrypt/settings.h>
  
  #ifdef WOLFSSL_MICROCHIP_PIC32MZ
  
  #ifdef NO_INLINE
      #include <wolfssl/wolfcrypt/misc.h>
  #else
      #define WOLFSSL_MISC_INCLUDED
      #include <wolfcrypt/src/misc.c>
  #endif
  
  #include <wolfssl/wolfcrypt/logging.h>
  #include <wolfssl/wolfcrypt/error-crypt.h>
  
  #include <wolfssl/wolfcrypt/port/pic32/pic32mz-crypt.h>
  
  #ifdef WOLFSSL_PIC32MZ_CRYPT
  #include <wolfssl/wolfcrypt/aes.h>
  #include <wolfssl/wolfcrypt/des3.h>
  #endif
  
  #ifdef WOLFSSL_PIC32MZ_HASH
  #include <wolfssl/wolfcrypt/md5.h>
  #include <wolfssl/wolfcrypt/sha.h>
  #include <wolfssl/wolfcrypt/sha256.h>
  #endif
  
  
  #if defined(WOLFSSL_PIC32MZ_CRYPT) || defined(WOLFSSL_PIC32MZ_HASH)
  
  static int Pic32GetBlockSize(int algo)
  {
      switch (algo) {
          case PIC32_ALGO_HMAC1:
              return PIC32_BLOCKSIZE_HMAC;
          case PIC32_ALGO_SHA256:
              return PIC32_BLOCKSIZE_SHA256;
          case PIC32_ALGO_SHA1:
              return PIC32_BLOCKSIZE_SHA1;
          case PIC32_ALGO_MD5:
              return PIC32_BLOCKSIZE_MD5;
          case PIC32_ALGO_AES:
              return PIC32_BLOCKSIZE_AES;
          case PIC32_ALGO_TDES:
              return PIC32_BLOCKSIZE_TDES;
          case PIC32_ALGO_DES:
              return PIC32_BLOCKSIZE_DES;
      }
      return 0;
  }
  
  static int Pic32Crypto(const byte* pIn, int inLen, word32* pOut, int outLen,
      int dir, int algo, int cryptoalgo,
  
      /* For DES/AES only */
      word32* key, int keyLen, word32* iv, int ivLen)
  {
      int ret = 0;
      int blockSize = Pic32GetBlockSize(algo);
      volatile bufferDescriptor bd __attribute__((aligned (8)));
      securityAssociation sa __attribute__((aligned (8)));
      securityAssociation *sa_p;
      bufferDescriptor *bd_p;
      byte *in_p;
      byte *out_p;
      word32* dst;
      word32 padRemain;
      int timeout = 0xFFFFFF;
      word32* in = (word32*)pIn;
      word32* out = pOut;
      int isDynamic = 0;
  
      /* check args */
      if (in == NULL || inLen <= 0 || out == NULL || blockSize == 0) {
          return BAD_FUNC_ARG;
      }
  
      /* check pointer alignment - must be word aligned */
      if (((size_t)in % sizeof(word32)) || ((size_t)out % sizeof(word32))) {
          /* dynamically allocate aligned pointers */
          isDynamic = 1;
          in = (word32*)XMALLOC(inLen, NULL, DYNAMIC_TYPE_AES_BUFFER);
          if (in == NULL)
              return MEMORY_E;
          if ((word32*)pIn == pOut) /* inline */
              out = (word32*)in;
          else {
              out = (word32*)XMALLOC(outLen, NULL, DYNAMIC_TYPE_AES_BUFFER);
              if (out == NULL) {
                  XFREE(in, NULL, DYNAMIC_TYPE_AES_BUFFER);
                  return MEMORY_E;
              }
          }
          XMEMCPY(in, pIn, inLen);
      }
  
      /* get uncached address */
      sa_p = KVA0_TO_KVA1(&sa);
      bd_p = KVA0_TO_KVA1(&bd);
      out_p= KVA0_TO_KVA1(out);
      in_p = KVA0_TO_KVA1(in);
  
      /* Sync cache if in physical memory (not flash) */
      if (PIC32MZ_IF_RAM(in_p)) {
          XMEMCPY(in_p, in, inLen);
      }
  
      /* Set up the Security Association */
      XMEMSET(sa_p, 0, sizeof(sa));
      sa_p->SA_CTRL.ALGO = algo;
      sa_p->SA_CTRL.ENCTYPE = dir;
      sa_p->SA_CTRL.FB = 1; /* first block */
      sa_p->SA_CTRL.LNC = 1; /* Load new set of keys */
      if (key) {
          /* cipher */
          sa_p->SA_CTRL.CRYPTOALGO = cryptoalgo;
  
          switch (keyLen) {
              case 32:
                  sa_p->SA_CTRL.KEYSIZE = PIC32_KEYSIZE_256;
                  break;
              case 24:
              case 8: /* DES */
                  sa_p->SA_CTRL.KEYSIZE = PIC32_KEYSIZE_192;
                  break;
              case 16:
                  sa_p->SA_CTRL.KEYSIZE = PIC32_KEYSIZE_128;
                  break;
          }
  
          dst = (word32*)KVA0_TO_KVA1(sa.SA_ENCKEY +
              (sizeof(sa.SA_ENCKEY)/sizeof(word32)) - (keyLen/sizeof(word32)));
          ByteReverseWords(dst, key, keyLen);
  
          if (iv && ivLen > 0) {
              sa_p->SA_CTRL.LOADIV = 1;
              dst = (word32*)KVA0_TO_KVA1(sa.SA_ENCIV +
                  (sizeof(sa.SA_ENCIV)/sizeof(word32)) - (ivLen/sizeof(word32)));
              ByteReverseWords(dst, iv, ivLen);
          }
      }
      else {
          /* hashing */
          sa_p->SA_CTRL.LOADIV = 1;
          sa_p->SA_CTRL.IRFLAG = 0; /* immediate result for hashing */
  
          dst = (word32*)KVA0_TO_KVA1(sa.SA_AUTHIV +
              (sizeof(sa.SA_AUTHIV)/sizeof(word32)) - (outLen/sizeof(word32)));
          ByteReverseWords(dst, out, outLen);
      }
  
      /* Set up the Buffer Descriptor */
      XMEMSET(bd_p, 0, sizeof(bd));
      bd_p->BD_CTRL.BUFLEN = inLen;
      padRemain = (inLen % 4); /* make sure buffer is 4-byte multiple */
      if (padRemain != 0) {
          bd_p->BD_CTRL.BUFLEN += (4 - padRemain);
      }
      bd_p->BD_CTRL.SA_FETCH_EN = 1; /* Fetch the security association */
      bd_p->BD_CTRL.PKT_INT_EN = 1;  /* enable interrupt */
      bd_p->BD_CTRL.LAST_BD = 1;     /* last buffer desc in chain */
      bd_p->BD_CTRL.LIFM = 1;        /* last in frame */
      bd_p->SA_ADDR = (unsigned int)KVA_TO_PA(&sa);
      bd_p->SRCADDR = (unsigned int)KVA_TO_PA(in);
      if (key) {
          /* cipher */
          if (in != out)
              XMEMSET(out_p, 0, outLen); /* clear output buffer */
          bd_p->DSTADDR = (unsigned int)KVA_TO_PA(out);
      }
      else {
          /* hashing */
          /* digest result returned in UPDPTR */
          bd_p->UPDPTR = (unsigned int)KVA_TO_PA(out);
      }
      bd_p->NXTPTR = (unsigned int)KVA_TO_PA(&bd);
      bd_p->MSGLEN = inLen;          /* actual message size */
      bd_p->BD_CTRL.DESC_EN = 1;     /* enable this descriptor */
  
      /* begin access to hardware */
      ret = wolfSSL_CryptHwMutexLock();
      if (ret == 0) {
          /* Software Reset the Crypto Engine */
          CECON = 1 << 6;
          while (CECON);
  
          /* Clear the interrupt flags */
          CEINTSRC = 0xF;
  
          /* Run the engine */
          CEBDPADDR = (unsigned int)KVA_TO_PA(&bd);
          CEINTEN = 0x07; /* enable DMA Packet Completion Interrupt */
  
          /* input swap, enable BD fetch and start DMA */
      #if PIC32_NO_OUT_SWAP
          CECON = 0x25;
      #else
          CECON = 0xa5; /* bit 7 = enable out swap */
      #endif
  
          /* wait for operation to complete */
          while (CEINTSRCbits.PKTIF == 0 && --timeout > 0) {};
  
          /* Clear the interrupt flags */
          CEINTSRC = 0xF;
  
          /* check for errors */
          if (CESTATbits.ERROP || timeout <= 0) {
          #if 0
              printf("PIC32 Crypto: ERROP %x, ERRPHASE %x, TIMEOUT %s\n",
                  CESTATbits.ERROP, CESTATbits.ERRPHASE, timeout <= 0 ? "yes" : "no");
          #endif
              ret = ASYNC_OP_E;
          }
  
          wolfSSL_CryptHwMutexUnLock();
  
          /* copy result to output */
      #if PIC32_NO_OUT_SWAP
          /* swap bytes */
          ByteReverseWords(out, (word32*)out_p, outLen);
      #elif defined(_SYS_DEVCON_LOCAL_H)
          /* sync cache */
          SYS_DEVCON_DataCacheInvalidate((word32)out, outLen);
      #else
          XMEMCPY(out, out_p, outLen);
      #endif
      }
  
      /* handle unaligned */
      if (isDynamic) {
          /* return result */
          XMEMCPY(pOut, out, outLen);
  
          /* free dynamic buffers */
          XFREE(in, NULL, DYNAMIC_TYPE_AES_BUFFER);
          if ((word32*)pIn != pOut)
              XFREE(out, NULL, DYNAMIC_TYPE_AES_BUFFER);
      }
  
      return ret;
  }
  #endif /* WOLFSSL_PIC32MZ_CRYPT || WOLFSSL_PIC32MZ_HASH */
  
  
  #ifdef WOLFSSL_PIC32MZ_HASH
  
  #ifdef WOLFSSL_PIC32MZ_LARGE_HASH
  
  /* tunable large hash block size */
  #ifndef PIC32_BLOCK_SIZE
      #define PIC32_BLOCK_SIZE 256
  #endif
  
  #define PIC32MZ_MIN_BLOCK    64
  #define PIC32MZ_MAX_BLOCK    (32*1024)
  
  #ifndef PIC32MZ_MAX_BD
      #define PIC32MZ_MAX_BD   2
  #endif
  
  #if PIC32_BLOCK_SIZE < PIC32MZ_MIN_BLOCK
      #error Encryption block size must be at least 64 bytes.
  #endif
  
  /* Crypt Engine descriptor */
  typedef struct {
      int currBd;
      int err;
      unsigned int msgSize;
      uint32_t processed;
      uint32_t dbPtr;
      int engine_ready;
      volatile bufferDescriptor   bd[PIC32MZ_MAX_BD] __attribute__((aligned (8)));
      securityAssociation         sa                 __attribute__((aligned (8)));
  } pic32mz_desc;
  
  static pic32mz_desc gLHDesc __attribute__((coherent));
  static uint8_t gLHDataBuf[PIC32MZ_MAX_BD][PIC32_BLOCK_SIZE] __attribute__((aligned (4), coherent));
  
  static void reset_engine(int algo)
  {
      int i;
  
      wolfSSL_CryptHwMutexLock();
  
      /* Software reset */
      CECON = 1 << 6;
      while (CECON);
  
      /* Clear the interrupt flags */
      CEINTSRC = 0xF;
  
      /* Make sure everything is clear first before we setup */
      XMEMSET(&gLHDesc, 0, sizeof(pic32mz_desc));
  
      /* Set up the Security Association */
      gLHDesc.sa.SA_CTRL.ALGO = algo;
      gLHDesc.sa.SA_CTRL.LNC = 1;
      gLHDesc.sa.SA_CTRL.FB = 1;
      gLHDesc.sa.SA_CTRL.ENCTYPE = 1;
      gLHDesc.sa.SA_CTRL.LOADIV = 1;
  
      /* Set up the Buffer Descriptor */
      gLHDesc.err = 0;
      for (i = 0; i < PIC32MZ_MAX_BD; i++) {
          XMEMSET((void *)&gLHDesc.bd[i], 0, sizeof(gLHDesc.bd[i]));
          gLHDesc.bd[i].BD_CTRL.LAST_BD = 1;
          gLHDesc.bd[i].BD_CTRL.LIFM = 1;
          gLHDesc.bd[i].BD_CTRL.PKT_INT_EN = 1;
          gLHDesc.bd[i].SA_ADDR = KVA_TO_PA(&gLHDesc.sa);
          gLHDesc.bd[i].SRCADDR = KVA_TO_PA(&gLHDataBuf[i]);
          if (PIC32MZ_MAX_BD > i+1)
              gLHDesc.bd[i].NXTPTR = KVA_TO_PA(&gLHDesc.bd[i+1]);
          else
              gLHDesc.bd[i].NXTPTR = KVA_TO_PA(&gLHDesc.bd[0]);
          XMEMSET((void *)&gLHDataBuf[i], 0, PIC32_BLOCK_SIZE);
      }
      gLHDesc.bd[0].BD_CTRL.SA_FETCH_EN = 1; /* Fetch the security association on the first BD */
      gLHDesc.dbPtr = 0;
      gLHDesc.currBd = 0;
      gLHDesc.msgSize = 0;
      gLHDesc.processed = 0;
      CEBDPADDR = KVA_TO_PA(&(gLHDesc.bd[0]));
  
      CEPOLLCON = 10;
  
  #if PIC32_NO_OUT_SWAP
      CECON = 0x27;
  #else
      CECON = 0xa7;
  #endif
  }
  
  static void update_engine(const byte *input, word32 len, word32 *hash)
  {
      int total;
      
      gLHDesc.bd[gLHDesc.currBd].UPDPTR = KVA_TO_PA(hash);
  
      /* Add the data to the current buffer. If the buffer fills, start processing it
         and fill the next one. */
      while (len) {
          /* If we've been given the message size, we can process along the
             way.
             Enable the current buffer descriptor if it is full. */
          if (gLHDesc.dbPtr >= PIC32_BLOCK_SIZE) {
              /* Wrap up the buffer descriptor and enable it so the engine can process */
              gLHDesc.bd[gLHDesc.currBd].MSGLEN = gLHDesc.msgSize;
              gLHDesc.bd[gLHDesc.currBd].BD_CTRL.BUFLEN = gLHDesc.dbPtr;
              gLHDesc.bd[gLHDesc.currBd].BD_CTRL.LAST_BD = 0;
              gLHDesc.bd[gLHDesc.currBd].BD_CTRL.LIFM = 0;
              gLHDesc.bd[gLHDesc.currBd].BD_CTRL.DESC_EN = 1;
              /* Move to the next buffer descriptor, or wrap around. */
              gLHDesc.currBd++;
              if (gLHDesc.currBd >= PIC32MZ_MAX_BD)
                  gLHDesc.currBd = 0;
              /* Wait until the engine has processed the new BD. */
              while (gLHDesc.bd[gLHDesc.currBd].BD_CTRL.DESC_EN);
              gLHDesc.bd[gLHDesc.currBd].UPDPTR = KVA_TO_PA(hash);
              gLHDesc.dbPtr = 0;
          }
          if (!PIC32MZ_IF_RAM(input)) {
              /* If we're inputting from flash, let the BD have
                 the address and max the buffer size */
              gLHDesc.bd[gLHDesc.currBd].SRCADDR = KVA_TO_PA(input);
              total = (len > PIC32MZ_MAX_BLOCK ? PIC32MZ_MAX_BLOCK : len);
              gLHDesc.dbPtr = total;
              len -= total;
              input += total;
          }
          else {
              if (len > PIC32_BLOCK_SIZE - gLHDesc.dbPtr) {
                  /* We have more data than can be put in the buffer. Fill what we can.*/
                  total = PIC32_BLOCK_SIZE - gLHDesc.dbPtr;
                  XMEMCPY(&gLHDataBuf[gLHDesc.currBd][gLHDesc.dbPtr], input, total);
                  len -= total;
                  gLHDesc.dbPtr = PIC32_BLOCK_SIZE;
                  input += total;
              }
              else {
                  /* Fill up what we have, but don't turn on the engine.*/
                  XMEMCPY(&gLHDataBuf[gLHDesc.currBd][gLHDesc.dbPtr], input, len);
                  gLHDesc.dbPtr += len;
                  len = 0;
              }
          }
      }
  }
  
  static void start_engine(void)
  {
      /* Wrap up the last buffer descriptor and enable it */
      int bufferLen;
  
      bufferLen = gLHDesc.dbPtr;
      if (bufferLen % 4)
          bufferLen = (bufferLen + 4) - (bufferLen % 4);
      /* initialize the MSGLEN on engine startup to avoid infinite loop when
       * length is less than 257 (size of PIC32_BLOCK_SIZE) */
      gLHDesc.bd[gLHDesc.currBd].MSGLEN = gLHDesc.msgSize;
      gLHDesc.bd[gLHDesc.currBd].BD_CTRL.BUFLEN = bufferLen;
      gLHDesc.bd[gLHDesc.currBd].BD_CTRL.LAST_BD = 1;
      gLHDesc.bd[gLHDesc.currBd].BD_CTRL.LIFM = 1;
      gLHDesc.bd[gLHDesc.currBd].BD_CTRL.DESC_EN = 1;
  }
  
  void wait_engine(char *hash, int hash_sz)
  {
      int i;
      unsigned int engineRunning;
  
      do {
          engineRunning = 0;
          for (i = 0; i < PIC32MZ_MAX_BD; i++) {
              engineRunning = engineRunning || gLHDesc.bd[i].BD_CTRL.DESC_EN;
          }
      } while (engineRunning);
  
  #if PIC32_NO_OUT_SWAP
      /* swap bytes */
      ByteReverseWords(hash, KVA0_TO_KVA1(hash), hash_sz);
  #else
      /* copy output - hardware already swapped */
      XMEMCPY(hash, KVA0_TO_KVA1(hash), hash_sz);
  #endif
  
      wolfSSL_CryptHwMutexUnLock();
  }
  
  #endif /* WOLFSSL_PIC32MZ_LARGE_HASH */
  
  int wc_Pic32Hash(const byte* in, int inLen, word32* out, int outLen, int algo)
  {
      return Pic32Crypto(in, inLen, out, outLen, PIC32_ENCRYPTION, algo, 0,
          NULL, 0, NULL, 0);
  }
  
  int wc_Pic32HashCopy(hashUpdCache* src, hashUpdCache* dst)
  {
      /* mark destination as copy, so cache->buf is not free'd */
      if (dst) {
          dst->isCopy = 1;
      }
      return 0;
  }
  
  static int wc_Pic32HashUpdate(hashUpdCache* cache, byte* stdBuf, int stdBufLen,
      word32* digest, int digestSz, const byte* data, int len, int algo, void* heap)
  {
      int ret = 0;
      word32 newLenUpd, newLenPad, padRemain;
      byte* newBuf;
      int isNewBuf = 0;
  
  #ifdef WOLFSSL_PIC32MZ_LARGE_HASH
      /* if final length is set then pass straight to hardware */
      if (cache->finalLen) {
          if (cache->bufLen == 0) {
              reset_engine(algo);
              gLHDesc.msgSize = cache->finalLen;
          }
          update_engine(data, len, digest);
          cache->bufLen += len; /* track progress for blockType */
          return 0;
      }
  #endif
  
      /* cache updates */
      /* calculate new len */
      newLenUpd = cache->updLen + len;
  
      /* calculate padded len - pad buffer at 64-bytes for hardware */
      newLenPad = newLenUpd;
      padRemain = (newLenUpd % PIC32_BLOCKSIZE_HASH);
      if (padRemain != 0) {
          newLenPad += (PIC32_BLOCKSIZE_HASH - padRemain);
      }
  
      /* determine buffer source */
      if (newLenPad <= stdBufLen) {
          /* use standard buffer */
          newBuf = stdBuf;
      }
      else if (newLenPad > cache->bufLen) {
          /* alloc buffer */
          newBuf = (byte*)XMALLOC(newLenPad, heap, DYNAMIC_TYPE_HASH_TMP);
          if (newBuf == NULL) {
              if (cache->buf != stdBuf && !cache->isCopy) {
                  XFREE(cache->buf, heap, DYNAMIC_TYPE_HASH_TMP);
                  cache->buf = NULL;
                  cache->updLen = cache->bufLen = 0;
              }
              return MEMORY_E;
          }
          isNewBuf = 1;
          cache->isCopy = 0; /* no longer using copy buffer */
      }
      else {
          /* use existing buffer */
          newBuf = cache->buf;
      }
      if (cache->buf && cache->updLen > 0) {
          XMEMCPY(newBuf, cache->buf, cache->updLen);
          if (isNewBuf && cache->buf != stdBuf) {
              XFREE(cache->buf, heap, DYNAMIC_TYPE_HASH_TMP);
              cache->buf = NULL;
          }
      }
      XMEMCPY(newBuf + cache->updLen, data, len);
  
      cache->buf = newBuf;
      cache->updLen = newLenUpd;
      cache->bufLen = newLenPad;
  
      return ret;
  }
  
  static int wc_Pic32HashFinal(hashUpdCache* cache, byte* stdBuf,
      word32* digest, byte* hash, int digestSz, int algo, void* heap)
  {
      int ret = 0;
  
      /* if room add the pad */
      if (cache->buf && cache->updLen < cache->bufLen) {
          cache->buf[cache->updLen] = 0x80;
      }
  
  #ifdef WOLFSSL_PIC32MZ_LARGE_HASH
      if (cache->finalLen) {
          /* Only submit to hardware if update data provided matches expected */
          if (cache->bufLen == cache->finalLen) {
              start_engine();
              wait_engine((char*)digest, digestSz);
              XMEMCPY(hash, digest, digestSz);
          }
          else {
              wolfSSL_CryptHwMutexUnLock();
              ret = BUFFER_E;
          }
          cache->finalLen = 0;
      }
      else
  #endif
      {
          if (cache->updLen == 0) {
              /* handle empty input */
              switch (algo) {
                  case PIC32_ALGO_SHA256: {
                      const char* sha256EmptyHash =
                          "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\x9a\xfb\xf4\xc8\x99\x6f\xb9"
                          "\x24\x27\xae\x41\xe4\x64\x9b\x93\x4c\xa4\x95\x99\x1b\x78\x52"
                          "\xb8\x55";
                      XMEMCPY(hash, sha256EmptyHash, digestSz);
                      break;
                  }
                  case PIC32_ALGO_SHA1: {
                      const char* shaEmptyHash =
                          "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d\x32\x55\xbf\xef\x95\x60\x18"
                          "\x90\xaf\xd8\x07\x09";
                      XMEMCPY(hash, shaEmptyHash, digestSz);
                      break;
                  }
                  case PIC32_ALGO_MD5: {
                      const char* md5EmptyHash =
                          "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\x09\x98\xec\xf8\x42"
                          "\x7e";
                      XMEMCPY(hash, md5EmptyHash, digestSz);
                      break;
                  }
              } /* switch */
          }
          else {
              ret = wc_Pic32Hash(cache->buf, cache->updLen, digest, digestSz, algo);
              if (ret == 0) {
                  XMEMCPY(hash, digest, digestSz);
              }
          }
  
          if (cache->buf && cache->buf != stdBuf && !cache->isCopy) {
              XFREE(cache->buf, heap, DYNAMIC_TYPE_HASH_TMP);
              cache->buf = NULL;
          }
      }
  
      cache->buf = NULL;
      cache->bufLen = cache->updLen = 0;
  
      return ret;
  }
  
  static void wc_Pic32HashFree(hashUpdCache* cache, void* heap)
  {
      if (cache && cache->buf && !cache->isCopy) {
          XFREE(cache->buf, heap, DYNAMIC_TYPE_HASH_TMP);
          cache->buf = NULL;
      }
  }
  
  /* API's for compatibility with Harmony wrappers - not used */
  #ifndef NO_MD5
      int wc_InitMd5_ex(wc_Md5* md5, void* heap, int devId)
      {
          if (md5 == NULL)
              return BAD_FUNC_ARG;
  
          XMEMSET(md5, 0, sizeof(wc_Md5));
          md5->heap = heap;
          (void)devId;
          return 0;
      }
      int wc_Md5Update(wc_Md5* md5, const byte* data, word32 len)
      {
          if (md5 == NULL || (data == NULL && len > 0))
              return BAD_FUNC_ARG;
          return wc_Pic32HashUpdate(&md5->cache, (byte*)md5->buffer,
              sizeof(md5->buffer), md5->digest, MD5_DIGEST_SIZE,
              data, len, PIC32_ALGO_MD5, md5->heap);
      }
      int wc_Md5Final(wc_Md5* md5, byte* hash)
      {
          int ret;
  
          if (md5 == NULL || hash == NULL)
              return BAD_FUNC_ARG;
  
          ret = wc_Pic32HashFinal(&md5->cache, (byte*)md5->buffer,
              md5->digest, hash, MD5_DIGEST_SIZE,
              PIC32_ALGO_MD5, md5->heap);
  
          wc_InitMd5_ex(md5, md5->heap, INVALID_DEVID);  /* reset state */
  
          return ret;
      }
      void wc_Md5SizeSet(wc_Md5* md5, word32 len)
      {
          if (md5) {
          #ifdef WOLFSSL_PIC32MZ_LARGE_HASH
              md5->cache.finalLen = len;
          #else
              (void)len;
          #endif
          }
      }
      void wc_Md5Pic32Free(wc_Md5* md5)
      {
          if (md5) {
              wc_Pic32HashFree(&md5->cache, md5->heap);
          }
      }
  #endif /* !NO_MD5 */
  #ifndef NO_SHA
      int wc_InitSha_ex(wc_Sha* sha, void* heap, int devId)
      {
          if (sha == NULL)
              return BAD_FUNC_ARG;
  
          XMEMSET(sha, 0, sizeof(wc_Sha));
          sha->heap = heap;
          (void)devId;
          return 0;
      }
      int wc_ShaUpdate(wc_Sha* sha, const byte* data, word32 len)
      {
          if (sha == NULL || (data == NULL && len > 0))
              return BAD_FUNC_ARG;
          return wc_Pic32HashUpdate(&sha->cache, (byte*)sha->buffer,
              sizeof(sha->buffer), sha->digest, SHA_DIGEST_SIZE,
              data, len, PIC32_ALGO_SHA1, sha->heap);
      }
      int wc_ShaFinal(wc_Sha* sha, byte* hash)
      {
          int ret;
  
          if (sha == NULL || hash == NULL)
              return BAD_FUNC_ARG;
  
          ret = wc_Pic32HashFinal(&sha->cache, (byte*)sha->buffer,
              sha->digest, hash, SHA_DIGEST_SIZE,
              PIC32_ALGO_SHA1, sha->heap);
  
          wc_InitSha_ex(sha, sha->heap, INVALID_DEVID);  /* reset state */
  
          return ret;
      }
      void wc_ShaSizeSet(wc_Sha* sha, word32 len)
      {
          if (sha) {
          #ifdef WOLFSSL_PIC32MZ_LARGE_HASH
              sha->cache.finalLen = len;
          #else
              (void)len;
          #endif
          }
      }
      void wc_ShaPic32Free(wc_Sha* sha)
      {
          if (sha) {
              wc_Pic32HashFree(&sha->cache, sha->heap);
          }
      }
  #endif /* !NO_SHA */
  #ifndef NO_SHA256
      int wc_InitSha256_ex(wc_Sha256* sha256, void* heap, int devId)
      {
          if (sha256 == NULL)
              return BAD_FUNC_ARG;
  
          XMEMSET(sha256, 0, sizeof(wc_Sha256));
          sha256->heap = heap;
          (void)devId;
          return 0;
      }
      int wc_Sha256Update(wc_Sha256* sha256, const byte* data, word32 len)
      {
          if (sha256 == NULL || (data == NULL && len > 0))
              return BAD_FUNC_ARG;
          return wc_Pic32HashUpdate(&sha256->cache, (byte*)sha256->buffer,
              sizeof(sha256->buffer), sha256->digest, SHA256_DIGEST_SIZE,
              data, len, PIC32_ALGO_SHA256, sha256->heap);
      }
      int wc_Sha256Final(wc_Sha256* sha256, byte* hash)
      {
          int ret;
  
          if (sha256 == NULL || hash == NULL)
              return BAD_FUNC_ARG;
  
          ret = wc_Pic32HashFinal(&sha256->cache, (byte*)sha256->buffer,
              sha256->digest, hash, SHA256_DIGEST_SIZE,
              PIC32_ALGO_SHA256, sha256->heap);
  
          wc_InitSha256_ex(sha256, sha256->heap, INVALID_DEVID);  /* reset state */
  
          return ret;
      }
      void wc_Sha256SizeSet(wc_Sha256* sha256, word32 len)
      {
          if (sha256) {
          #ifdef WOLFSSL_PIC32MZ_LARGE_HASH
              sha256->cache.finalLen = len;
          #else
              (void)len;
          #endif
          }
      }
      void wc_Sha256Pic32Free(wc_Sha256* sha256)
      {
          if (sha256) {
              wc_Pic32HashFree(&sha256->cache, sha256->heap);
          }
      }
  #endif /* !NO_SHA256 */
  #endif /* WOLFSSL_PIC32MZ_HASH */
  
  
  #ifdef WOLFSSL_PIC32MZ_CRYPT
  #if !defined(NO_AES)
      int wc_Pic32AesCrypt(word32 *key, int keyLen, word32 *iv, int ivLen,
          byte* out, const byte* in, word32 sz,
          int dir, int algo, int cryptoalgo)
      {
          return Pic32Crypto(in, sz, (word32*)out, sz, dir, algo, cryptoalgo,
              key, keyLen, iv, ivLen);
      }
  #endif /* !NO_AES */
  
  #ifndef NO_DES3
      int wc_Pic32DesCrypt(word32 *key, int keyLen, word32 *iv, int ivLen,
          byte* out, const byte* in, word32 sz,
          int dir, int algo, int cryptoalgo)
      {
          return Pic32Crypto(in, sz, (word32*)out, sz, dir, algo, cryptoalgo,
              key, keyLen, iv, ivLen);
      }
  #endif /* !NO_DES3 */
  #endif /* WOLFSSL_PIC32MZ_CRYPT */
  
  #endif /* WOLFSSL_MICROCHIP_PIC32MZ */
  