/* evp.c
 *
 * Copyright (C) 2006-2019 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 <wolfssl/wolfcrypt/settings.h>

#if !defined(WOLFSSL_EVP_INCLUDED)
    #ifndef WOLFSSL_IGNORE_FILE_WARN
        #warning evp.c does not need to be compiled seperatly from ssl.c
    #endif
#else

static unsigned int cipherType(const WOLFSSL_EVP_CIPHER *cipher);


/* Getter function for cipher key length
 *
 * c  WOLFSSL_EVP_CIPHER structure to get key length from
 *
 * NOTE: OpenSSL_add_all_ciphers() should be called first before using this
 *       function
 *
 * Returns size of key in bytes
 */
int wolfSSL_EVP_Cipher_key_length(const WOLFSSL_EVP_CIPHER* c)
{
    WOLFSSL_ENTER("wolfSSL_EVP_Cipher_key_length");

    if (c == NULL) {
        return 0;
    }

    switch (cipherType(c)) {
    #if !defined(NO_AES) && defined(HAVE_AES_CBC)
      case AES_128_CBC_TYPE: return 16;
      case AES_192_CBC_TYPE: return 24;
      case AES_256_CBC_TYPE: return 32;
  #endif
  #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
      case AES_128_CTR_TYPE: return 16;
      case AES_192_CTR_TYPE: return 24;
      case AES_256_CTR_TYPE: return 32;
  #endif
  #if !defined(NO_AES) && defined(HAVE_AES_ECB)
      case AES_128_ECB_TYPE: return 16;
      case AES_192_ECB_TYPE: return 24;
      case AES_256_ECB_TYPE: return 32;
  #endif
  #ifndef NO_DES3
      case DES_CBC_TYPE:      return 8;
      case DES_EDE3_CBC_TYPE: return 24;
      case DES_ECB_TYPE:      return 8;
      case DES_EDE3_ECB_TYPE: return 24;
  #endif
      default:
          return 0;
      }
}


WOLFSSL_API int  wolfSSL_EVP_EncryptInit(WOLFSSL_EVP_CIPHER_CTX* ctx,
                                        const WOLFSSL_EVP_CIPHER* type,
                                        const unsigned char* key,
                                        const unsigned char* iv)
{
    return wolfSSL_EVP_CipherInit(ctx, type, (byte*)key, (byte*)iv, 1);
}

WOLFSSL_API int  wolfSSL_EVP_EncryptInit_ex(WOLFSSL_EVP_CIPHER_CTX* ctx,
                                        const WOLFSSL_EVP_CIPHER* type,
                                        WOLFSSL_ENGINE *impl,
                                        const unsigned char* key,
                                        const unsigned char* iv)
{
    (void) impl;
    return wolfSSL_EVP_CipherInit(ctx, type, (byte*)key, (byte*)iv, 1);
}

WOLFSSL_API int  wolfSSL_EVP_DecryptInit(WOLFSSL_EVP_CIPHER_CTX* ctx,
                                        const WOLFSSL_EVP_CIPHER* type,
                                        const unsigned char* key,
                                        const unsigned char* iv)
{
    WOLFSSL_ENTER("wolfSSL_EVP_CipherInit");
    return wolfSSL_EVP_CipherInit(ctx, type, (byte*)key, (byte*)iv, 0);
}

WOLFSSL_API int  wolfSSL_EVP_DecryptInit_ex(WOLFSSL_EVP_CIPHER_CTX* ctx,
                                        const WOLFSSL_EVP_CIPHER* type,
                                        WOLFSSL_ENGINE *impl,
                                        const unsigned char* key,
                                        const unsigned char* iv)
{
    (void) impl;
    WOLFSSL_ENTER("wolfSSL_EVP_DecryptInit");
    return wolfSSL_EVP_CipherInit(ctx, type, (byte*)key, (byte*)iv, 0);
}


WOLFSSL_API WOLFSSL_EVP_CIPHER_CTX *wolfSSL_EVP_CIPHER_CTX_new(void)
{
	WOLFSSL_EVP_CIPHER_CTX *ctx = (WOLFSSL_EVP_CIPHER_CTX*)XMALLOC(sizeof *ctx,
                                                 NULL, DYNAMIC_TYPE_TMP_BUFFER);
	if (ctx) {
      WOLFSSL_ENTER("wolfSSL_EVP_CIPHER_CTX_new");
		  wolfSSL_EVP_CIPHER_CTX_init(ctx);
  }
	return ctx;
}

WOLFSSL_API void wolfSSL_EVP_CIPHER_CTX_free(WOLFSSL_EVP_CIPHER_CTX *ctx)
{
    if (ctx) {
        WOLFSSL_ENTER("wolfSSL_EVP_CIPHER_CTX_free");
		    wolfSSL_EVP_CIPHER_CTX_cleanup(ctx);
		    XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
		}
}

WOLFSSL_API unsigned long wolfSSL_EVP_CIPHER_CTX_mode(const WOLFSSL_EVP_CIPHER_CTX *ctx)
{
  if (ctx == NULL) return 0;
  return ctx->flags & WOLFSSL_EVP_CIPH_MODE;
}

WOLFSSL_API int  wolfSSL_EVP_EncryptFinal(WOLFSSL_EVP_CIPHER_CTX *ctx,
                                   unsigned char *out, int *outl)
{
    if (ctx && ctx->enc) {
        WOLFSSL_ENTER("wolfSSL_EVP_EncryptFinal");
        return wolfSSL_EVP_CipherFinal(ctx, out, outl);
    }
    else
        return WOLFSSL_FAILURE;
}


WOLFSSL_API int  wolfSSL_EVP_CipherInit_ex(WOLFSSL_EVP_CIPHER_CTX* ctx,
                                    const WOLFSSL_EVP_CIPHER* type,
                                    WOLFSSL_ENGINE *impl,
                                    const unsigned char* key,
                                    const unsigned char* iv,
                                    int enc)
{
    (void)impl;
    return wolfSSL_EVP_CipherInit(ctx, type, key, iv, enc);
}

WOLFSSL_API int  wolfSSL_EVP_EncryptFinal_ex(WOLFSSL_EVP_CIPHER_CTX *ctx,
                                   unsigned char *out, int *outl)
{
    if (ctx && ctx->enc) {
        WOLFSSL_ENTER("wolfSSL_EVP_EncryptFinal_ex");
        return wolfSSL_EVP_CipherFinal(ctx, out, outl);
    }
    else
        return WOLFSSL_FAILURE;
}

WOLFSSL_API int  wolfSSL_EVP_DecryptFinal(WOLFSSL_EVP_CIPHER_CTX *ctx,
                                   unsigned char *out, int *outl)
{
    if (ctx && ctx->enc) {
        WOLFSSL_ENTER("wolfSSL_EVP_DecryptFinal");
        return wolfSSL_EVP_CipherFinal(ctx, out, outl);
    }
    else {
        return WOLFSSL_FAILURE;
    }
}

WOLFSSL_API int  wolfSSL_EVP_DecryptFinal_ex(WOLFSSL_EVP_CIPHER_CTX *ctx,
                                   unsigned char *out, int *outl)
{
    if (ctx && ctx->enc) {
        WOLFSSL_ENTER("wolfSSL_EVP_DecryptFinal_ex");
        return wolfSSL_EVP_CipherFinal(ctx, out, outl);
    }
    else {
        return WOLFSSL_FAILURE;
    }
}


WOLFSSL_API int wolfSSL_EVP_DigestInit_ex(WOLFSSL_EVP_MD_CTX* ctx,
                                     const WOLFSSL_EVP_MD* type,
                                     WOLFSSL_ENGINE *impl)
{
    (void) impl;
    WOLFSSL_ENTER("wolfSSL_EVP_DigestInit_ex");
    return wolfSSL_EVP_DigestInit(ctx, type);
}

#ifdef DEBUG_WOLFSSL_EVP
#define PRINT_BUF(b, sz) { int _i; for(_i=0; _i<(sz); _i++) { \
  printf("%02x(%c),", (b)[_i], (b)[_i]); if ((_i+1)%8==0)printf("\n");}}
#else
#define PRINT_BUF(b, sz)
#endif

static int fillBuff(WOLFSSL_EVP_CIPHER_CTX *ctx, const unsigned char *in, int sz)
{
    int fill;

    if (sz > 0) {
        if ((sz+ctx->bufUsed) > ctx->block_size) {
            fill = ctx->block_size - ctx->bufUsed;
        } else {
            fill = sz;
        }
        XMEMCPY(&(ctx->buf[ctx->bufUsed]), in, fill);
        ctx->bufUsed += fill;
        return fill;
    } else return 0;
}

static int evpCipherBlock(WOLFSSL_EVP_CIPHER_CTX *ctx,
                                   unsigned char *out,
                                   const unsigned char *in, int inl)
{
    int ret = 0;

    switch (ctx->cipherType) {
    #if !defined(NO_AES) && defined(HAVE_AES_CBC)
        case AES_128_CBC_TYPE:
        case AES_192_CBC_TYPE:
        case AES_256_CBC_TYPE:
            if (ctx->enc)
                ret = wc_AesCbcEncrypt(&ctx->cipher.aes, out, in, inl);
            else
                ret = wc_AesCbcDecrypt(&ctx->cipher.aes, out, in, inl);
            break;
    #endif
    #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
        case AES_128_CTR_TYPE:
        case AES_192_CTR_TYPE:
        case AES_256_CTR_TYPE:
            ret = wc_AesCtrEncrypt(&ctx->cipher.aes, out, in, inl);
            break;
    #endif
    #if !defined(NO_AES) && defined(HAVE_AES_ECB)
        case AES_128_ECB_TYPE:
        case AES_192_ECB_TYPE:
        case AES_256_ECB_TYPE:
            if (ctx->enc)
                ret = wc_AesEcbEncrypt(&ctx->cipher.aes, out, in, inl);
            else
                ret = wc_AesEcbDecrypt(&ctx->cipher.aes, out, in, inl);
            break;
    #endif
    #ifndef NO_DES3
        case DES_CBC_TYPE:
            if (ctx->enc)
                ret = wc_Des_CbcEncrypt(&ctx->cipher.des, out, in, inl);
            else
                ret = wc_Des_CbcDecrypt(&ctx->cipher.des, out, in, inl);
            break;
        case DES_EDE3_CBC_TYPE:
            if (ctx->enc)
                ret = wc_Des3_CbcEncrypt(&ctx->cipher.des3, out, in, inl);
            else
                ret = wc_Des3_CbcDecrypt(&ctx->cipher.des3, out, in, inl);
            break;
        #if defined(WOLFSSL_DES_ECB)
        case DES_ECB_TYPE:
            ret = wc_Des_EcbEncrypt(&ctx->cipher.des, out, in, inl);
            break;
        case DES_EDE3_ECB_TYPE:
            ret = wc_Des3_EcbEncrypt(&ctx->cipher.des3, out, in, inl);
            break;
        #endif
    #endif
    #ifndef NO_RC4
        case ARC4_TYPE:
            wc_Arc4Process(&ctx->cipher.arc4, out, in, inl);
        break;
    #endif
        default:
            return WOLFSSL_FAILURE;
    }

    if (ret != 0)
        return WOLFSSL_FAILURE; /* failure */

    (void)in;
    (void)inl;
    (void)out;

    return WOLFSSL_SUCCESS; /* success */
}

WOLFSSL_API int wolfSSL_EVP_CipherUpdate(WOLFSSL_EVP_CIPHER_CTX *ctx,
                                   unsigned char *out, int *outl,
                                   const unsigned char *in, int inl)
{
    int blocks;
    int fill;

    if ((ctx == NULL) || (inl < 0) ||
        (outl == NULL)|| (out == NULL) || (in == NULL)) return BAD_FUNC_ARG;
    WOLFSSL_ENTER("wolfSSL_EVP_CipherUpdate");

    *outl = 0;
    if (inl == 0) return WOLFSSL_SUCCESS;

    if (ctx->bufUsed > 0) { /* concatinate them if there is anything */
        fill = fillBuff(ctx, in, inl);
        inl -= fill;
        in  += fill;
    }
    if ((ctx->enc == 0)&& (ctx->lastUsed == 1)) {
        PRINT_BUF(ctx->lastBlock, ctx->block_size);
        XMEMCPY(out, ctx->lastBlock, ctx->block_size);
        *outl+= ctx->block_size;
        out  += ctx->block_size;
    }
    if (ctx->bufUsed == ctx->block_size) {
        /* the buff is full, flash out */
        PRINT_BUF(ctx->buf, ctx->block_size);
        if (evpCipherBlock(ctx, out, ctx->buf, ctx->block_size) == 0)
            return WOLFSSL_FAILURE;
        PRINT_BUF(out, ctx->block_size);
        if (ctx->enc == 0) {
            ctx->lastUsed = 1;
            XMEMCPY(ctx->lastBlock, out, ctx->block_size);
        } else {
            *outl+= ctx->block_size;
            out  += ctx->block_size;
        }
        ctx->bufUsed = 0;
    }

    blocks = inl / ctx->block_size;
    if (blocks > 0) {
        /* process blocks */
        if (evpCipherBlock(ctx, out, in, blocks * ctx->block_size) == 0)
            return WOLFSSL_FAILURE;
        PRINT_BUF(in, ctx->block_size*blocks);
        PRINT_BUF(out,ctx->block_size*blocks);
        inl  -= ctx->block_size * blocks;
        in   += ctx->block_size * blocks;
        if (ctx->enc == 0) {
            if ((ctx->flags & WOLFSSL_EVP_CIPH_NO_PADDING) ||
                    (ctx->block_size == 1)) {
                ctx->lastUsed = 0;
                *outl+= ctx->block_size * blocks;
            } else {
                if (inl == 0) {
                    ctx->lastUsed = 1;
                    blocks = blocks - 1; /* save last block to check padding in
                                          * EVP_CipherFinal call */
                    XMEMCPY(ctx->lastBlock, &out[ctx->block_size * blocks],
                            ctx->block_size);
                }
                *outl+= ctx->block_size * blocks;
            }
        } else {
            *outl+= ctx->block_size * blocks;
        }
    }
    if (inl > 0) {
        /* put fraction into buff */
        fillBuff(ctx, in, inl);
        /* no increase of outl */
    }

    (void)out; /* silence warning in case not read */

    return WOLFSSL_SUCCESS;
}

static void padBlock(WOLFSSL_EVP_CIPHER_CTX *ctx)
{
    int i;
    for (i = ctx->bufUsed; i < ctx->block_size; i++)
        ctx->buf[i] = (byte)(ctx->block_size - ctx->bufUsed);
}

static int checkPad(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *buff)
{
    int i;
    int n;
    n = buff[ctx->block_size-1];
    if (n > ctx->block_size) return -1;
    for (i = 0; i < n; i++) {
        if (buff[ctx->block_size-i-1] != n)
            return -1;
    }
    return ctx->block_size - n;
}

WOLFSSL_API int  wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx,
                                   unsigned char *out, int *outl)
{
    int fl;
    if (ctx == NULL || out == NULL || outl == NULL)
        return BAD_FUNC_ARG;

    WOLFSSL_ENTER("wolfSSL_EVP_CipherFinal");
    if (ctx->flags & WOLFSSL_EVP_CIPH_NO_PADDING) {
        if (ctx->bufUsed != 0) return WOLFSSL_FAILURE;
        *outl = 0;
        return WOLFSSL_SUCCESS;
    }
    if (ctx->enc) {
        if (ctx->block_size == 1) {
            *outl = 0;
            return WOLFSSL_SUCCESS;
        }
        if ((ctx->bufUsed >= 0) && (ctx->block_size != 1)) {
            padBlock(ctx);
            PRINT_BUF(ctx->buf, ctx->block_size);
            if (evpCipherBlock(ctx, out, ctx->buf, ctx->block_size) == 0)
                return WOLFSSL_FAILURE;

            PRINT_BUF(out, ctx->block_size);
            *outl = ctx->block_size;
        }
    } else {
        if (ctx->block_size == 1) {
            *outl = 0;
            return WOLFSSL_SUCCESS;
        }
        if ((ctx->bufUsed % ctx->block_size) != 0) {
            *outl = 0;
            /* not enough padding for decrypt */
            return WOLFSSL_FAILURE;
        }
        if (ctx->lastUsed) {
            PRINT_BUF(ctx->lastBlock, ctx->block_size);
            if ((fl = checkPad(ctx, ctx->lastBlock)) >= 0) {
                XMEMCPY(out, ctx->lastBlock, fl);
                *outl = fl;
            }
            else {
                return WOLFSSL_FAILURE;
            }
        }
       /* return error in cases where the block length is incorrect */
        if (ctx->lastUsed == 0 && ctx->bufUsed == 0) {
            return WOLFSSL_FAILURE;
        }
    }
    return WOLFSSL_SUCCESS;
}


#ifdef WOLFSSL_EVP_DECRYPT_LEGACY
/* This is a version of DecryptFinal to work with data encrypted with
 * wolfSSL_EVP_EncryptFinal() with the broken padding. (pre-v3.12.0)
 * Only call this after wolfSSL_EVP_CipherFinal() fails on a decrypt.
 * Note, you don't know if the padding is good or bad with the old
 * encrypt, but it is likely to be or bad. It will update the output
 * length with the block_size so the last block is still captured. */
WOLFSSL_API int  wolfSSL_EVP_DecryptFinal_legacy(WOLFSSL_EVP_CIPHER_CTX *ctx,
        unsigned char *out, int *outl)
{
    int fl;
    if (ctx == NULL || out == NULL || outl == NULL)
        return BAD_FUNC_ARG;

    WOLFSSL_ENTER("wolfSSL_EVP_DecryptFinal_legacy");
    if (ctx->block_size == 1) {
        *outl = 0;
        return WOLFSSL_SUCCESS;
    }
    if ((ctx->bufUsed % ctx->block_size) != 0) {
        *outl = 0;
        /* not enough padding for decrypt */
        return WOLFSSL_FAILURE;
    }
    /* The original behavior of CipherFinal() was like it is now,
     * but checkPad would return 0 in case of a bad pad. It would
     * treat the pad as 0, and leave the data in the output buffer,
     * and not try to copy anything. This converts checkPad's -1 error
     * code to block_size.
     */
    if (ctx->lastUsed) {
        PRINT_BUF(ctx->lastBlock, ctx->block_size);
        if ((fl = checkPad(ctx, ctx->lastBlock)) < 0) {
            fl = ctx->block_size;
        }
        else {
            XMEMCPY(out, ctx->lastBlock, fl);
        }
        *outl = fl;
    }
    /* return error in cases where the block length is incorrect */
    if (ctx->lastUsed == 0 && ctx->bufUsed == 0) {
        return WOLFSSL_FAILURE;
    }

    return WOLFSSL_SUCCESS;
}
#endif


WOLFSSL_API int wolfSSL_EVP_CIPHER_CTX_block_size(const WOLFSSL_EVP_CIPHER_CTX *ctx)
{
    if (ctx == NULL) return BAD_FUNC_ARG;
    switch (ctx->cipherType) {
#if !defined(NO_AES) || !defined(NO_DES3)
#if !defined(NO_AES) && defined(HAVE_AES_CBC)
    case AES_128_CBC_TYPE:
    case AES_192_CBC_TYPE:
    case AES_256_CBC_TYPE:
#endif
#if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
    case AES_128_CTR_TYPE:
    case AES_192_CTR_TYPE:
    case AES_256_CTR_TYPE:
#endif
#if !defined(NO_AES)
    case AES_128_ECB_TYPE:
    case AES_192_ECB_TYPE:
    case AES_256_ECB_TYPE:
#endif
#ifndef NO_DES3
    case DES_CBC_TYPE:
    case DES_ECB_TYPE:
    case DES_EDE3_CBC_TYPE:
    case DES_EDE3_ECB_TYPE:
#endif
        return ctx->block_size;
#endif /* !NO_AES || !NO_DES3 */
    default:
        return 0;
    }
}

static unsigned int cipherType(const WOLFSSL_EVP_CIPHER *cipher)
{
    if (cipher == NULL) return 0; /* dummy for #ifdef */
  #ifndef NO_DES3
      else if (XSTRNCMP(cipher, EVP_DES_CBC, EVP_DES_SIZE) == 0)
          return DES_CBC_TYPE;
      else if (XSTRNCMP(cipher, EVP_DES_EDE3_CBC, EVP_DES_EDE3_SIZE) == 0)
          return DES_EDE3_CBC_TYPE;
  #if !defined(NO_DES3)
      else if (XSTRNCMP(cipher, EVP_DES_ECB, EVP_DES_SIZE) == 0)
          return DES_ECB_TYPE;
      else if (XSTRNCMP(cipher, EVP_DES_EDE3_ECB, EVP_DES_EDE3_SIZE) == 0)
          return DES_EDE3_ECB_TYPE;
  #endif /* NO_DES3 && HAVE_AES_ECB */
  #endif

  #if !defined(NO_AES) && defined(HAVE_AES_CBC)
      #ifdef WOLFSSL_AES_128
      else if (XSTRNCMP(cipher, EVP_AES_128_CBC, EVP_AES_SIZE) == 0)
          return AES_128_CBC_TYPE;
      #endif
      #ifdef WOLFSSL_AES_192
      else if (XSTRNCMP(cipher, EVP_AES_192_CBC, EVP_AES_SIZE) == 0)
          return AES_192_CBC_TYPE;
      #endif
      #ifdef WOLFSSL_AES_256
      else if (XSTRNCMP(cipher, EVP_AES_256_CBC, EVP_AES_SIZE) == 0)
          return AES_256_CBC_TYPE;
      #endif
  #endif /* !NO_AES && HAVE_AES_CBC */
  #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
      #ifdef WOLFSSL_AES_128
      else if (XSTRNCMP(cipher, EVP_AES_128_CTR, EVP_AES_SIZE) == 0)
          return AES_128_CTR_TYPE;
      #endif
      #ifdef WOLFSSL_AES_192
      else if (XSTRNCMP(cipher, EVP_AES_192_CTR, EVP_AES_SIZE) == 0)
          return AES_192_CTR_TYPE;
      #endif
      #ifdef WOLFSSL_AES_256
      else if (XSTRNCMP(cipher, EVP_AES_256_CTR, EVP_AES_SIZE) == 0)
          return AES_256_CTR_TYPE;
      #endif
  #endif /* !NO_AES && HAVE_AES_CBC */
  #if !defined(NO_AES) && defined(HAVE_AES_ECB)
      #ifdef WOLFSSL_AES_128
      else if (XSTRNCMP(cipher, EVP_AES_128_ECB, EVP_AES_SIZE) == 0)
          return AES_128_ECB_TYPE;
      #endif
      #ifdef WOLFSSL_AES_192
      else if (XSTRNCMP(cipher, EVP_AES_192_ECB, EVP_AES_SIZE) == 0)
          return AES_192_ECB_TYPE;
      #endif
      #ifdef WOLFSSL_AES_256
      else if (XSTRNCMP(cipher, EVP_AES_256_ECB, EVP_AES_SIZE) == 0)
          return AES_256_ECB_TYPE;
      #endif
  #endif /* !NO_AES && HAVE_AES_CBC */
      else return 0;
}

WOLFSSL_API int wolfSSL_EVP_CIPHER_block_size(const WOLFSSL_EVP_CIPHER *cipher)
{
  if (cipher == NULL) return BAD_FUNC_ARG;
  switch (cipherType(cipher)) {
  #if !defined(NO_AES) && defined(HAVE_AES_CBC)
      case AES_128_CBC_TYPE:
      case AES_192_CBC_TYPE:
      case AES_256_CBC_TYPE:
                             return AES_BLOCK_SIZE;
  #endif
  #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
      case AES_128_CTR_TYPE:
      case AES_192_CTR_TYPE:
      case AES_256_CTR_TYPE:
                             return AES_BLOCK_SIZE;
  #endif
  #if !defined(NO_AES) && defined(HAVE_AES_ECB)
      case AES_128_ECB_TYPE:
      case AES_192_ECB_TYPE:
      case AES_256_ECB_TYPE:
                             return AES_BLOCK_SIZE;
  #endif
  #ifndef NO_DES3
      case DES_CBC_TYPE: return 8;
      case DES_EDE3_CBC_TYPE: return 8;
      case DES_ECB_TYPE: return 8;
      case DES_EDE3_ECB_TYPE: return 8;
  #endif
      default:
          return 0;
      }
}

unsigned long WOLFSSL_CIPHER_mode(const WOLFSSL_EVP_CIPHER *cipher)
{
    switch (cipherType(cipher)) {
    #if !defined(NO_AES) && defined(HAVE_AES_CBC)
        case AES_128_CBC_TYPE:
        case AES_192_CBC_TYPE:
        case AES_256_CBC_TYPE:
            return WOLFSSL_EVP_CIPH_CBC_MODE;
    #endif
    #if !defined(NO_AES) && defined(WOLFSSL_AES_COUNTER)
        case AES_128_CTR_TYPE:
        case AES_192_CTR_TYPE:
        case AES_256_CTR_TYPE:
            return WOLFSSL_EVP_CIPH_CTR_MODE;
    #endif
    #if !defined(NO_AES)
        case AES_128_ECB_TYPE:
        case AES_192_ECB_TYPE:
        case AES_256_ECB_TYPE:
            return WOLFSSL_EVP_CIPH_ECB_MODE;
    #endif
    #ifndef NO_DES3
        case DES_CBC_TYPE:
        case DES_EDE3_CBC_TYPE:
            return WOLFSSL_EVP_CIPH_CBC_MODE;
        case DES_ECB_TYPE:
        case DES_EDE3_ECB_TYPE:
            return WOLFSSL_EVP_CIPH_ECB_MODE;
    #endif
    #ifndef NO_RC4
        case ARC4_TYPE:
            return EVP_CIPH_STREAM_CIPHER;
    #endif
        default:
            return 0;
        }
}

WOLFSSL_API unsigned long WOLFSSL_EVP_CIPHER_mode(const WOLFSSL_EVP_CIPHER *cipher)
{
  if (cipher == NULL) return 0;
  return WOLFSSL_CIPHER_mode(cipher);
}

WOLFSSL_API void wolfSSL_EVP_CIPHER_CTX_set_flags(WOLFSSL_EVP_CIPHER_CTX *ctx, int flags)
{
    if (ctx != NULL) {
        ctx->flags |= flags;
    }
}

WOLFSSL_API void wolfSSL_EVP_CIPHER_CTX_clear_flags(WOLFSSL_EVP_CIPHER_CTX *ctx, int flags)
{
    if (ctx != NULL) {
        ctx->flags &= ~flags;
    }
}

WOLFSSL_API unsigned long wolfSSL_EVP_CIPHER_flags(const WOLFSSL_EVP_CIPHER *cipher)
{
  if (cipher == NULL) return 0;
  return WOLFSSL_CIPHER_mode(cipher);
}

WOLFSSL_API int  wolfSSL_EVP_CIPHER_CTX_set_padding(WOLFSSL_EVP_CIPHER_CTX *ctx, int padding)
{
  if (ctx == NULL) return BAD_FUNC_ARG;
  if (padding) {
      ctx->flags &= ~WOLFSSL_EVP_CIPH_NO_PADDING;
  }
  else {
      ctx->flags |=  WOLFSSL_EVP_CIPH_NO_PADDING;
  }
  return 1;
}

WOLFSSL_API int wolfSSL_EVP_add_digest(const WOLFSSL_EVP_MD *digest)
{
    (void)digest;
    /* nothing to do */
    return 0;
}


/* Frees the WOLFSSL_EVP_PKEY_CTX passed in.
 *
 * return WOLFSSL_SUCCESS on success
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_free(WOLFSSL_EVP_PKEY_CTX *ctx)
{
    if (ctx == NULL) return 0;
    WOLFSSL_ENTER("EVP_PKEY_CTX_free");
    XFREE(ctx, NULL, DYNAMIC_TYPE_PUBLIC_KEY);
    return WOLFSSL_SUCCESS;
}


/* Creates a new WOLFSSL_EVP_PKEY_CTX structure.
 *
 * pkey  key structure to use with new WOLFSSL_EVP_PEKY_CTX
 * e     engine to use. It should be NULL at this time.
 *
 * return the new structure on success and NULL if failed.
 */
WOLFSSL_API WOLFSSL_EVP_PKEY_CTX *wolfSSL_EVP_PKEY_CTX_new(WOLFSSL_EVP_PKEY *pkey, WOLFSSL_ENGINE *e)
{
    WOLFSSL_EVP_PKEY_CTX* ctx;

    if (pkey == NULL) return 0;
    if (e != NULL) return 0;
    WOLFSSL_ENTER("EVP_PKEY_CTX_new");

    ctx = (WOLFSSL_EVP_PKEY_CTX*)XMALLOC(sizeof(WOLFSSL_EVP_PKEY_CTX), NULL,
            DYNAMIC_TYPE_PUBLIC_KEY);
    if (ctx == NULL) return NULL;
    XMEMSET(ctx, 0, sizeof(WOLFSSL_EVP_PKEY_CTX));
    ctx->pkey = pkey;
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
    ctx->padding = RSA_PKCS1_PADDING;
#endif

    return ctx;
}


/* Sets the type of RSA padding to use.
 *
 * ctx     structure to set padding in.
 * padding RSA padding type
 *
 * returns WOLFSSL_SUCCESS on success.
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_set_rsa_padding(WOLFSSL_EVP_PKEY_CTX *ctx, int padding)
{
    if (ctx == NULL) return 0;
    WOLFSSL_ENTER("EVP_PKEY_CTX_set_rsa_padding");
    ctx->padding = padding;
    return WOLFSSL_SUCCESS;
}


/* Uses the WOLFSSL_EVP_PKEY_CTX to decrypt a buffer.
 *
 * ctx    structure to decrypt with
 * out    buffer to hold the results
 * outlen initially holds size of out buffer and gets set to decrypt result size
 * in     buffer decrypt
 * inlen  length of in buffer
 *
 * returns WOLFSSL_SUCCESS on success.
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_decrypt(WOLFSSL_EVP_PKEY_CTX *ctx,
                     unsigned char *out, size_t *outlen,
                     const unsigned char *in, size_t inlen)
{
    int len = 0;

    if (ctx == NULL) return 0;
    WOLFSSL_ENTER("EVP_PKEY_decrypt");

    (void)out;
    (void)outlen;
    (void)in;
    (void)inlen;
    (void)len;

    switch (ctx->pkey->type) {
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
    case EVP_PKEY_RSA:
        len = wolfSSL_RSA_private_decrypt((int)inlen, (unsigned char*)in, out,
              ctx->pkey->rsa, ctx->padding);
        if (len < 0) break;
        else {
            *outlen = len;
            return WOLFSSL_SUCCESS;
        }
#endif /* NO_RSA */

    case EVP_PKEY_EC:
        WOLFSSL_MSG("not implemented");
        FALL_THROUGH;
    default:
        break;
    }
    return WOLFSSL_FAILURE;
}


/* Initialize a WOLFSSL_EVP_PKEY_CTX structure for decryption
 *
 * ctx    WOLFSSL_EVP_PKEY_CTX structure to use with decryption
 *
 * Returns WOLFSSL_FAILURE on failure and WOLFSSL_SUCCESS on success
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_decrypt_init(WOLFSSL_EVP_PKEY_CTX *ctx)
{
    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_PKEY_decrypt_init");
    switch (ctx->pkey->type) {
    case EVP_PKEY_RSA:
        ctx->op = EVP_PKEY_OP_DECRYPT;
        return WOLFSSL_SUCCESS;
    case EVP_PKEY_EC:
        WOLFSSL_MSG("not implemented");
        FALL_THROUGH;
    default:
        break;
    }
    return WOLFSSL_FAILURE;
}


/* Use a WOLFSSL_EVP_PKEY_CTX structure to encrypt data
 *
 * ctx    WOLFSSL_EVP_PKEY_CTX structure to use with encryption
 * out    buffer to hold encrypted data
 * outlen length of out buffer
 * in     data to be encrypted
 * inlen  length of in buffer
 *
 * Returns WOLFSSL_FAILURE on failure and WOLFSSL_SUCCESS on success
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_encrypt(WOLFSSL_EVP_PKEY_CTX *ctx,
                     unsigned char *out, size_t *outlen,
                     const unsigned char *in, size_t inlen)
{
    int len = 0;
    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_PKEY_encrypt");
    if (ctx->op != EVP_PKEY_OP_ENCRYPT) return WOLFSSL_FAILURE;

    (void)out;
    (void)outlen;
    (void)in;
    (void)inlen;
    (void)len;
    switch (ctx->pkey->type) {
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
    case EVP_PKEY_RSA:
        len = wolfSSL_RSA_public_encrypt((int)inlen, (unsigned char *)in, out,
                  ctx->pkey->rsa, ctx->padding);
        if (len < 0)
            break;
        else {
            *outlen = len;
            return WOLFSSL_SUCCESS;
        }
#endif /* NO_RSA */

    case EVP_PKEY_EC:
        WOLFSSL_MSG("not implemented");
        FALL_THROUGH;
    default:
        break;
    }
    return WOLFSSL_FAILURE;
}


/* Initialize a WOLFSSL_EVP_PKEY_CTX structure to encrypt data
 *
 * ctx    WOLFSSL_EVP_PKEY_CTX structure to use with encryption
 *
 * Returns WOLFSSL_FAILURE on failure and WOLFSSL_SUCCESS on success
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_encrypt_init(WOLFSSL_EVP_PKEY_CTX *ctx)
{
    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_PKEY_encrypt_init");

    switch (ctx->pkey->type) {
    case EVP_PKEY_RSA:
        ctx->op = EVP_PKEY_OP_ENCRYPT;
        return WOLFSSL_SUCCESS;
    case EVP_PKEY_EC:
        WOLFSSL_MSG("not implemented");
        FALL_THROUGH;
    default:
        break;
    }
    return WOLFSSL_FAILURE;
}


/* Get the size in bits for WOLFSSL_EVP_PKEY key
 *
 * pkey WOLFSSL_EVP_PKEY structure to get key size of
 *
 * returns the size in bits of key on success
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_bits(const WOLFSSL_EVP_PKEY *pkey)
{
    int bytes;

    if (pkey == NULL) return 0;
    WOLFSSL_ENTER("EVP_PKEY_bits");
    if ((bytes = wolfSSL_EVP_PKEY_size((WOLFSSL_EVP_PKEY*)pkey)) ==0) return 0;
    return bytes*8;
}


/* Get the size in bytes for WOLFSSL_EVP_PKEY key
 *
 * pkey WOLFSSL_EVP_PKEY structure to get key size of
 *
 * returns the size of a key on success which is the maximum size of a
 *         signature
 */
WOLFSSL_API int wolfSSL_EVP_PKEY_size(WOLFSSL_EVP_PKEY *pkey)
{
    if (pkey == NULL) return 0;
    WOLFSSL_ENTER("EVP_PKEY_size");

    switch (pkey->type) {
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
    case EVP_PKEY_RSA:
        return (int)wolfSSL_RSA_size((const WOLFSSL_RSA*)(pkey->rsa));
#endif /* NO_RSA */

#ifdef HAVE_ECC
    case EVP_PKEY_EC:
        if (pkey->ecc == NULL || pkey->ecc->internal == NULL) {
            WOLFSSL_MSG("No ECC key has been set");
            break;
        }
        return wc_ecc_size((ecc_key*)(pkey->ecc->internal));
#endif /* HAVE_ECC */

    default:
        break;
    }
    return 0;
}


/* Initialize structure for signing
 *
 * ctx  WOLFSSL_EVP_MD_CTX structure to initialize
 * type is the type of message digest to use
 *
 * returns WOLFSSL_SUCCESS on success
 */
WOLFSSL_API int wolfSSL_EVP_SignInit(WOLFSSL_EVP_MD_CTX *ctx, const WOLFSSL_EVP_MD *type)
{
    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_SignInit");
    return wolfSSL_EVP_DigestInit(ctx,type);
}


/* Update structure with data for signing
 *
 * ctx  WOLFSSL_EVP_MD_CTX structure to update
 * data buffer holding data to update with for sign
 * len  length of data buffer
 *
 * returns WOLFSSL_SUCCESS on success
 */
WOLFSSL_API int wolfSSL_EVP_SignUpdate(WOLFSSL_EVP_MD_CTX *ctx, const void *data, size_t len)
{
    if (ctx == NULL) return 0;
    WOLFSSL_ENTER("EVP_SignUpdate(");
    return wolfSSL_EVP_DigestUpdate(ctx, data, len);
}

/* macro gaurd because currently only used with RSA */
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
/* Helper function for getting the NID value from md
 *
 * returns the NID value associated with md on success */
static int md2nid(const unsigned char md)
{
    const char * d;
    d = (const char *)wolfSSL_EVP_get_md(md);
    if (XSTRNCMP(d, "SHA", 3) == 0) {
        if (XSTRLEN(d) > 3) {
            if (XSTRNCMP(d, "SHA256", 6) == 0) {
                return NID_sha256;
            }
            if (XSTRNCMP(d, "SHA384", 6) == 0) {
                return NID_sha384;
            }
            if (XSTRNCMP(d, "SHA512", 6) == 0) {
                return NID_sha512;
            }
            WOLFSSL_MSG("Unknown SHA type");
            return 0;
        }
        else {
            return NID_sha1;
        }
    }
    if (XSTRNCMP(d, "MD5", 3) == 0)
        return NID_md5;
    return 0;
}
#endif /* NO_RSA */

/* Finalize structure for signing
 *
 * ctx    WOLFSSL_EVP_MD_CTX structure to finalize
 * sigret buffer to hold resulting signature
 * siglen length of sigret buffer
 * pkey   key to sign with
 *
 * returns WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure
 */
WOLFSSL_API int wolfSSL_EVP_SignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sigret,
                  unsigned int *siglen, WOLFSSL_EVP_PKEY *pkey)
{
    unsigned int mdsize;
    unsigned char md[WC_MAX_DIGEST_SIZE];
    int ret;
    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_SignFinal");

    ret = wolfSSL_EVP_DigestFinal(ctx, md, &mdsize);
    if (ret <= 0) return ret;

    (void)sigret;
    (void)siglen;

    switch (pkey->type) {
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
    case EVP_PKEY_RSA: {
        int nid = md2nid(ctx->macType);
        if (nid < 0) break;
        return wolfSSL_RSA_sign(nid, md, mdsize, sigret,
                                siglen, pkey->rsa);
    }
#endif /* NO_RSA */

    case EVP_PKEY_DSA:
    case EVP_PKEY_EC:
        WOLFSSL_MSG("not implemented");
        FALL_THROUGH;
    default:
        break;
    }
    return WOLFSSL_FAILURE;
}


/* Initialize structure for verifying signature
 *
 * ctx  WOLFSSL_EVP_MD_CTX structure to initialize
 * type is the type of message digest to use
 *
 * returns WOLFSSL_SUCCESS on success
 */
WOLFSSL_API int wolfSSL_EVP_VerifyInit(WOLFSSL_EVP_MD_CTX *ctx, const WOLFSSL_EVP_MD *type)
{
    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_VerifyInit");
    return wolfSSL_EVP_DigestInit(ctx,type);
}


/* Update structure for verifying signature
 *
 * ctx  WOLFSSL_EVP_MD_CTX structure to update
 * data buffer holding data to update with for verify
 * len  length of data buffer
 *
 * returns WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure
 */
WOLFSSL_API int wolfSSL_EVP_VerifyUpdate(WOLFSSL_EVP_MD_CTX *ctx, const void *data, size_t len)
{
    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_VerifyUpdate");
    return wolfSSL_EVP_DigestUpdate(ctx, data, len);
}


/* Finalize structure for verifying signature
 *
 * ctx    WOLFSSL_EVP_MD_CTX structure to finalize
 * sig    buffer holding signature
 * siglen length of sig buffer
 * pkey   key to verify with
 *
 * returns WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure
 */
WOLFSSL_API int wolfSSL_EVP_VerifyFinal(WOLFSSL_EVP_MD_CTX *ctx,
        unsigned char*sig, unsigned int siglen, WOLFSSL_EVP_PKEY *pkey)
{
    int ret;
    unsigned char md[WC_MAX_DIGEST_SIZE];
    unsigned int mdsize;

    if (ctx == NULL) return WOLFSSL_FAILURE;
    WOLFSSL_ENTER("EVP_VerifyFinal");
    ret = wolfSSL_EVP_DigestFinal(ctx, md, &mdsize);
    if (ret <= 0) return ret;

    (void)sig;
    (void)siglen;

    switch (pkey->type) {
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
    case EVP_PKEY_RSA: {
        int nid = md2nid(ctx->macType);
        if (nid < 0) break;
        return wolfSSL_RSA_verify(nid, md, mdsize, sig,
                (unsigned int)siglen, pkey->rsa);
    }
#endif /* NO_RSA */

    case EVP_PKEY_DSA:
    case EVP_PKEY_EC:
        WOLFSSL_MSG("not implemented");
        FALL_THROUGH;
    default:
        break;
    }
    return WOLFSSL_FAILURE;
}

WOLFSSL_API int wolfSSL_EVP_add_cipher(const WOLFSSL_EVP_CIPHER *cipher)
{
    (void)cipher;
    /* nothing to do */
    return 0;
}


WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_mac_key(int type, ENGINE* e,
                                          const unsigned char* key, int keylen)
{
    WOLFSSL_EVP_PKEY* pkey;

    (void)e;

    if (type != EVP_PKEY_HMAC || (key == NULL && keylen != 0))
        return NULL;

    pkey = wolfSSL_PKEY_new();
    if (pkey != NULL) {
        pkey->pkey.ptr = (char*)XMALLOC(keylen, NULL, DYNAMIC_TYPE_PUBLIC_KEY);
        if (pkey->pkey.ptr == NULL && keylen > 0) {
            wolfSSL_EVP_PKEY_free(pkey);
            pkey = NULL;
        }
        else {
            XMEMCPY(pkey->pkey.ptr, key, keylen);
            pkey->pkey_sz = keylen;
            pkey->type = pkey->save_type = type;
        }
    }

    return pkey;
}


const unsigned char* wolfSSL_EVP_PKEY_get0_hmac(const WOLFSSL_EVP_PKEY* pkey,
                                                size_t* len)
{
    if (pkey == NULL || len == NULL)
        return NULL;

    *len = (size_t)pkey->pkey_sz;

    return (const unsigned char*)pkey->pkey.ptr;
}


/* Initialize an EVP_DigestSign/Verify operation.
 * Initialize a digest for RSA and ECC keys, or HMAC for HMAC key.
 */
static int wolfSSL_evp_digest_pk_init(WOLFSSL_EVP_MD_CTX *ctx,
                                      WOLFSSL_EVP_PKEY_CTX **pctx,
                                      const WOLFSSL_EVP_MD *type,
                                      WOLFSSL_ENGINE *e,
                                      WOLFSSL_EVP_PKEY *pkey)
{
    if (pkey->type == EVP_PKEY_HMAC) {
        int                  hashType;
        const unsigned char* key;
        size_t               keySz;

        if (XSTRNCMP(type, "SHA256", 6) == 0) {
            hashType = WC_SHA256;
        }
    #ifdef WOLFSSL_SHA224
        else if (XSTRNCMP(type, "SHA224", 6) == 0) {
            hashType = WC_SHA224;
        }
    #endif
    #ifdef WOLFSSL_SHA384
        else if (XSTRNCMP(type, "SHA384", 6) == 0) {
            hashType = WC_SHA384;
        }
    #endif
    #ifdef WOLFSSL_SHA512
        else if (XSTRNCMP(type, "SHA512", 6) == 0) {
            hashType = WC_SHA512;
        }
    #endif
    #ifndef NO_MD5
        else if (XSTRNCMP(type, "MD5", 3) == 0) {
            hashType = WC_MD5;
        }
    #endif
    #ifndef NO_SHA
        /* has to be last since would pick or 224, 256, 384, or 512 too */
        else if (XSTRNCMP(type, "SHA", 3) == 0) {
             hashType = WC_SHA;
        }
    #endif /* NO_SHA */
        else
             return BAD_FUNC_ARG;

        key = wolfSSL_EVP_PKEY_get0_hmac(pkey, &keySz);

        if (wc_HmacInit(&ctx->hash.hmac, NULL, INVALID_DEVID) != 0)
            return WOLFSSL_FAILURE;

        if (wc_HmacSetKey(&ctx->hash.hmac, hashType, key, (word32)keySz) != 0)
            return WOLFSSL_FAILURE;

        ctx->macType = NID_hmac & 0xFF;
    }
    else {
        int ret;

        if (ctx->pctx == NULL)
            ctx->pctx = wolfSSL_EVP_PKEY_CTX_new(pkey, e);
        if (ctx->pctx == NULL)
            return WOLFSSL_FAILURE;

        ret = wolfSSL_EVP_DigestInit(ctx, type);
        if (ret == WOLFSSL_SUCCESS && pctx != NULL)
            *pctx = ctx->pctx;
        return ret;
    }

    return WOLFSSL_SUCCESS;
}

/* Update an EVP_DigestSign/Verify operation.
 * Update a digest for RSA and ECC keys, or HMAC for HMAC key.
 */
static int wolfssl_evp_digest_pk_update(WOLFSSL_EVP_MD_CTX *ctx,
                                        const void *d, unsigned int cnt)
{
    if (ctx->pctx == NULL) {
        if (ctx->macType != (NID_hmac & 0xFF))
            return WOLFSSL_FAILURE;

        if (wc_HmacUpdate(&ctx->hash.hmac, (const byte *)d, cnt) != 0)
            return WOLFSSL_FAILURE;

        return WOLFSSL_SUCCESS;
    }
    else
        return wolfSSL_EVP_DigestUpdate(ctx, d, cnt);
}

/* Finalize an EVP_DigestSign/Verify operation - common part only.
 * Finalize a digest for RSA and ECC keys, or HMAC for HMAC key.
 * Copies the digest so that you can keep updating.
 */
static int wolfssl_evp_digest_pk_final(WOLFSSL_EVP_MD_CTX *ctx,
                                       unsigned char *md, unsigned int* mdlen)
{
    int  ret;

    if (ctx->pctx == NULL) {
        Hmac hmacCopy;

        if (ctx->macType != (NID_hmac & 0xFF))
            return WOLFSSL_FAILURE;

        if (wolfSSL_HmacCopy(&hmacCopy, &ctx->hash.hmac) != WOLFSSL_SUCCESS)
            return WOLFSSL_FAILURE;
        ret = wc_HmacFinal(&hmacCopy, md) == 0;
        wc_HmacFree(&hmacCopy);
        return ret;
    }
    else {
        WOLFSSL_EVP_MD_CTX ctxCopy;

        if (wolfSSL_EVP_MD_CTX_copy_ex(&ctxCopy, ctx) != WOLFSSL_SUCCESS)
            return WOLFSSL_FAILURE;

        ret = wolfSSL_EVP_DigestFinal(&ctxCopy, md, mdlen);
        wolfSSL_EVP_MD_CTX_cleanup(&ctxCopy);
        return ret;
    }
}

/* Get the length of the mac based on the digest algorithm. */
static int wolfssl_mac_len(unsigned char macType)
{
    int hashLen;

    switch (macType) {
    #ifndef NO_MD5
        case WC_MD5:
            hashLen = WC_MD5_DIGEST_SIZE;
            break;
    #endif /* !NO_MD5 */

    #ifndef NO_SHA
        case WC_SHA:
            hashLen = WC_SHA_DIGEST_SIZE;
            break;
    #endif /* !NO_SHA */

    #ifdef WOLFSSL_SHA224
        case WC_SHA224:
            hashLen = WC_SHA224_DIGEST_SIZE;
            break;
    #endif /* WOLFSSL_SHA224 */

    #ifndef NO_SHA256
        case WC_SHA256:
            hashLen = WC_SHA256_DIGEST_SIZE;
            break;
    #endif /* !NO_SHA256 */

    #ifdef WOLFSSL_SHA384
        case WC_SHA384:
            hashLen = WC_SHA384_DIGEST_SIZE;
            break;
    #endif /* WOLFSSL_SHA384 */
    #ifdef WOLFSSL_SHA512
        case WC_SHA512:
            hashLen = WC_SHA512_DIGEST_SIZE;
            break;
    #endif /* WOLFSSL_SHA512 */

    #ifdef HAVE_BLAKE2
        case BLAKE2B_ID:
            hashLen = BLAKE2B_OUTBYTES;
            break;
    #endif /* HAVE_BLAKE2 */

        default:
            hashLen = 0;
    }

    return hashLen;
}

int wolfSSL_EVP_DigestSignInit(WOLFSSL_EVP_MD_CTX *ctx,
                               WOLFSSL_EVP_PKEY_CTX **pctx,
                               const WOLFSSL_EVP_MD *type,
                               WOLFSSL_ENGINE *e,
                               WOLFSSL_EVP_PKEY *pkey)
{
    WOLFSSL_ENTER("EVP_DigestSignInit");

    if (ctx == NULL || type == NULL || pkey == NULL)
        return BAD_FUNC_ARG;

    return wolfSSL_evp_digest_pk_init(ctx, pctx, type, e, pkey);
}


int wolfSSL_EVP_DigestSignUpdate(WOLFSSL_EVP_MD_CTX *ctx, const void *d,
                                 unsigned int cnt)
{
    WOLFSSL_ENTER("EVP_DigestSignUpdate");

    if (ctx == NULL || d == NULL)
        return BAD_FUNC_ARG;

    return wolfssl_evp_digest_pk_update(ctx, d, cnt);
}

int wolfSSL_EVP_DigestSignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sig,
                                size_t *siglen)
{
    unsigned char digest[WC_MAX_DIGEST_SIZE];
    unsigned int  hashLen;
    int           ret = WOLFSSL_FAILURE;

    WOLFSSL_ENTER("EVP_DigestSignFinal");

    if (ctx == NULL || siglen == NULL)
        return WOLFSSL_FAILURE;

    /* Return the maximum size of the signaure when sig is NULL. */
    if (ctx->pctx == NULL) {
        if (ctx->macType != (NID_hmac & 0xFF))
            return WOLFSSL_FAILURE;

        hashLen = wolfssl_mac_len(ctx->hash.hmac.macType);

        if (sig == NULL) {
            *siglen = hashLen;
            return WOLFSSL_SUCCESS;
        }
    }
#if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
    else if (ctx->pctx->pkey->type == EVP_PKEY_RSA) {
        if (sig == NULL) {
            *siglen = wolfSSL_RSA_size(ctx->pctx->pkey->rsa);
            return WOLFSSL_SUCCESS;
        }
    }
#endif
#ifdef HAVE_ECC
    else if (ctx->pctx->pkey->type == EVP_PKEY_EC) {
        if (sig == NULL) {
            /* SEQ + INT + INT */
            *siglen = ecc_sets[ctx->pctx->pkey->ecc->group->curve_idx].size * 2
                    + 8;
            return WOLFSSL_SUCCESS;
        }
    }
#endif

    if (wolfssl_evp_digest_pk_final(ctx, digest, &hashLen) <= 0)
        return WOLFSSL_FAILURE;

    if (ctx->pctx == NULL) {
        /* Copy the HMAC result as signature. */
        if ((unsigned int)(*siglen) > hashLen)
            *siglen = hashLen;
        /* May be a truncated signature. */

        XMEMCPY(sig, digest, *siglen);
        ret = WOLFSSL_SUCCESS;
    }
    else {
        /* Sign the digest. */
        switch (ctx->pctx->pkey->type) {
    #if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
        case EVP_PKEY_RSA: {
            unsigned int sigSz;
            int          nid = md2nid(ctx->macType);
            if (nid < 0)
                break;
            ret = wolfSSL_RSA_sign(nid, digest, hashLen, sig, &sigSz,
                                   ctx->pctx->pkey->rsa);
            if (ret >= 0)
                *siglen = sigSz;
            break;
        }
    #endif /* NO_RSA */

    #ifdef HAVE_ECC
        case EVP_PKEY_EC: {
            WOLFSSL_ECDSA_SIG *ecdsaSig;
            ecdsaSig = wolfSSL_ECDSA_do_sign(digest, hashLen,
                                             ctx->pctx->pkey->ecc);
            if (ecdsaSig == NULL)
                break;
            *siglen = wolfSSL_i2d_ECDSA_SIG(ecdsaSig, &sig);
            wolfSSL_ECDSA_SIG_free(ecdsaSig);
            ret = WOLFSSL_SUCCESS;
            break;
        }
    #endif
        default:
            break;
        }
    }

    ForceZero(digest, sizeof(digest));
    return ret;
}

int wolfSSL_EVP_DigestVerifyInit(WOLFSSL_EVP_MD_CTX *ctx,
                                 WOLFSSL_EVP_PKEY_CTX **pctx,
                                 const WOLFSSL_EVP_MD *type,
                                 WOLFSSL_ENGINE *e,
                                 WOLFSSL_EVP_PKEY *pkey)
{
    WOLFSSL_ENTER("EVP_DigestVerifyInit");

    if (ctx == NULL || type == NULL || pkey == NULL)
        return BAD_FUNC_ARG;

    return wolfSSL_evp_digest_pk_init(ctx, pctx, type, e, pkey);
}


int wolfSSL_EVP_DigestVerifyUpdate(WOLFSSL_EVP_MD_CTX *ctx, const void *d,
                                   size_t cnt)
{
    WOLFSSL_ENTER("EVP_DigestVerifyUpdate");

    if (ctx == NULL || d == NULL)
        return BAD_FUNC_ARG;

    return wolfssl_evp_digest_pk_update(ctx, d, (unsigned int)cnt);
}


int wolfSSL_EVP_DigestVerifyFinal(WOLFSSL_EVP_MD_CTX *ctx,
                                  const unsigned char *sig, size_t siglen)
{
    unsigned char digest[WC_MAX_DIGEST_SIZE];
    unsigned int  hashLen;

    WOLFSSL_ENTER("EVP_DigestVerifyFinal");

    if (ctx == NULL || sig == NULL)
        return WOLFSSL_FAILURE;

    if (ctx->pctx == NULL) {
        if (ctx->macType != (NID_hmac & 0xFF))
            return WOLFSSL_FAILURE;

        hashLen = wolfssl_mac_len(ctx->hash.hmac.macType);

        if (siglen > hashLen)
            return WOLFSSL_FAILURE;
        /* May be a truncated signature. */
    }

    if (wolfssl_evp_digest_pk_final(ctx, digest, &hashLen) <= 0)
        return WOLFSSL_FAILURE;

    if (ctx->pctx == NULL) {
        /* Check HMAC result matches the signature. */
        if (XMEMCMP(sig, digest, siglen) == 0)
            return WOLFSSL_SUCCESS;
        return WOLFSSL_FAILURE;
    }
    else {
        /* Verify the signature with the digest. */
        switch (ctx->pctx->pkey->type) {
    #if !defined(NO_RSA) && !defined(HAVE_USER_RSA)
        case EVP_PKEY_RSA: {
            int nid = md2nid(ctx->macType);
            if (nid < 0)
                return WOLFSSL_FAILURE;
            return wolfSSL_RSA_verify(nid, digest, hashLen, sig,
                                      (unsigned int)siglen,
                                      ctx->pctx->pkey->rsa);
        }
    #endif /* NO_RSA */

    #ifdef HAVE_ECC
        case EVP_PKEY_EC: {
            int ret;
            WOLFSSL_ECDSA_SIG *ecdsaSig;
            ecdsaSig = wolfSSL_d2i_ECDSA_SIG(NULL, &sig, (long)siglen);
            if (ecdsaSig == NULL)
                return WOLFSSL_FAILURE;
            ret = wolfSSL_ECDSA_do_verify(digest, hashLen, ecdsaSig,
                                          ctx->pctx->pkey->ecc);
            wolfSSL_ECDSA_SIG_free(ecdsaSig);
            return ret;
        }
    #endif
        default:
            break;
        }
    }

    return WOLFSSL_FAILURE;
}
#endif /* WOLFSSL_EVP_INCLUDED */

#if defined(OPENSSL_EXTRA) && !defined(NO_PWDBASED) && !defined(NO_SHA)
WOLFSSL_API int wolfSSL_PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
                                               const unsigned char *salt,
                                               int saltlen, int iter,
                                               int keylen, unsigned char *out)
{
    const char *nostring = "";
    int ret = 0;

    if (pass == NULL) {
        passlen = 0;
        pass = nostring;
    } else if (passlen == -1) {
        passlen = (int)XSTRLEN(pass);
    }

    ret = wc_PBKDF2((byte*)out, (byte*)pass, passlen, (byte*)salt, saltlen,
                    iter, keylen, WC_SHA);
    if (ret == 0)
        return WOLFSSL_SUCCESS;
    else
        return WOLFSSL_FAILURE;
}
#endif /* OPENSSL_EXTRA && !NO_PWDBASED !NO_SHA*/
