int wc_ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg,
                word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx)
{
    int          ret = 0;
    word32       blockSz;
    word32       digestSz;
    ecEncCtx     localCtx;
#ifdef WOLFSSL_SMALL_STACK
    byte*        sharedSecret;
    byte*        keys;
#else
    byte         sharedSecret[ECC_MAXSIZE];  /* 521 max size */
    byte         keys[ECC_BUFSIZE];         /* max size */
#endif
    word32       sharedSz = ECC_MAXSIZE;
    int          keysLen;
    int          encKeySz;
    int          ivSz;
    int          offset = 0;       /* in case using msg exchange */
    byte*        encKey;
    byte*        encIv;
    byte*        macKey;

    if (privKey == NULL || pubKey == NULL || msg == NULL || out == NULL ||
                           outSz  == NULL)
        return BAD_FUNC_ARG;

    if (ctx == NULL) {  /* use defaults */
        ecc_ctx_init(&localCtx, 0);
        ctx = &localCtx;
    }

    ret = ecc_get_key_sizes(ctx, &encKeySz, &ivSz, &keysLen, &digestSz,
                            &blockSz);
    if (ret != 0)
        return ret;

    if (ctx->protocol == REQ_RESP_CLIENT) {
        offset = keysLen;
        keysLen *= 2;

        if (ctx->cliSt != ecCLI_SENT_REQ)
            return BAD_STATE_E;

        ctx->cliSt = ecSRV_BAD_STATE; /* we're done no more ops allowed */
    }
    else if (ctx->protocol == REQ_RESP_SERVER) {
        if (ctx->srvSt != ecSRV_SALT_SET)
            return BAD_STATE_E;

        ctx->srvSt = ecSRV_RECV_REQ; /* only do this once */
    }

    if (keysLen > ECC_BUFSIZE) /* keys size */
        return BUFFER_E;

    if ( ((msgSz-digestSz) % blockSz) != 0)
        return BAD_PADDING_E;

    if (*outSz < (msgSz - digestSz))
        return BUFFER_E;

#ifdef WOLFSSL_SMALL_STACK
    sharedSecret = (byte*)XMALLOC(ECC_MAXSIZE, NULL, DYNAMIC_TYPE_ECC_BUFFER);
    if (sharedSecret == NULL)
        return MEMORY_E;

    keys = (byte*)XMALLOC(ECC_BUFSIZE, NULL, DYNAMIC_TYPE_ECC_BUFFER);
    if (keys == NULL) {
        XFREE(sharedSecret, NULL, DYNAMIC_TYPE_ECC_BUFFER);
        return MEMORY_E;
    }
#endif

    do {
    #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC)
        ret = wc_AsyncWait(ret, &privKey->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
        if (ret != 0)
            break;
    #endif
        ret = wc_ecc_shared_secret(privKey, pubKey, sharedSecret, &sharedSz);
    } while (ret == WC_PENDING_E);
    if (ret == 0) {
       switch (ctx->kdfAlgo) {
           case ecHKDF_SHA256 :
               ret = wc_HKDF(WC_SHA256, sharedSecret, sharedSz, ctx->kdfSalt,
                          ctx->kdfSaltSz, ctx->kdfInfo, ctx->kdfInfoSz,
                          keys, keysLen);
               break;

           default:
               ret = BAD_FUNC_ARG;
               break;
       }
    }

    if (ret == 0) {
       encKey = keys + offset;
       encIv  = encKey + encKeySz;
       macKey = encKey + encKeySz + ivSz;

       switch (ctx->macAlgo) {
           case ecHMAC_SHA256:
           {
               byte verify[WC_SHA256_DIGEST_SIZE];
               Hmac hmac;

               ret = wc_HmacInit(&hmac, NULL, INVALID_DEVID);
               if (ret == 0) {
                   ret = wc_HmacSetKey(&hmac, WC_SHA256, macKey, WC_SHA256_DIGEST_SIZE);
                   if (ret == 0)
                       ret = wc_HmacUpdate(&hmac, msg, msgSz-digestSz);
                   if (ret == 0)
                       ret = wc_HmacUpdate(&hmac, ctx->macSalt, ctx->macSaltSz);
                   if (ret == 0)
                       ret = wc_HmacFinal(&hmac, verify);
                   if (ret == 0) {
                      if (XMEMCMP(verify, msg + msgSz - digestSz, digestSz) != 0)
                          ret = -1;
                   }

                   wc_HmacFree(&hmac);
               }
               break;
           }

           default:
               ret = BAD_FUNC_ARG;
               break;
       }
    }

    if (ret == 0) {
       switch (ctx->encAlgo) {
    #ifdef HAVE_AES_CBC
           case ecAES_128_CBC:
               {
                   Aes aes;
                   ret = wc_AesSetKey(&aes, encKey, KEY_SIZE_128, encIv,
                                                                AES_DECRYPTION);
                   if (ret != 0)
                       break;
                   ret = wc_AesCbcDecrypt(&aes, out, msg, msgSz-digestSz);
                #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_AES)
                   ret = wc_AsyncWait(ret, &aes.asyncDev, WC_ASYNC_FLAG_NONE);
                #endif
               }
               break;
    #endif
           default:
               ret = BAD_FUNC_ARG;
               break;
       }
    }

    if (ret == 0)
       *outSz = msgSz - digestSz;

#ifdef WOLFSSL_SMALL_STACK
    XFREE(sharedSecret, NULL, DYNAMIC_TYPE_ECC_BUFFER);
    XFREE(keys, NULL, DYNAMIC_TYPE_ECC_BUFFER);
#endif

    return ret;
}