int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng,
                     ecc_key* key, mp_int *r, mp_int *s)
{
   int    err = 0;
#ifndef WOLFSSL_SP_MATH
   mp_int* e;
#if (!defined(WOLFSSL_ASYNC_CRYPT) || !defined(HAVE_CAVIUM_V)) && \
                                                   !defined(WOLFSSL_SMALL_STACK)
   mp_int  e_lcl;
#endif

#if defined(WOLFSSL_ECDSA_SET_K) || \
    (defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC) && \
    (defined(HAVE_CAVIUM_V) || defined(HAVE_INTEL_QA)))
   DECLARE_CURVE_SPECS(curve, ECC_CURVE_FIELD_COUNT);
#else
   DECLARE_CURVE_SPECS(curve, 1);
#endif
#endif /* !WOLFSSL_SP_MATH */

   if (in == NULL || r == NULL || s == NULL || key == NULL || rng == NULL) {
       return ECC_BAD_ARG_E;
   }

   /* is this a private key? */
   if (key->type != ECC_PRIVATEKEY && key->type != ECC_PRIVATEKEY_ONLY) {
      return ECC_BAD_ARG_E;
   }

   /* is the IDX valid ?  */
   if (wc_ecc_is_valid_idx(key->idx) != 1) {
      return ECC_BAD_ARG_E;
   }

#ifdef WOLFSSL_SP_MATH
#ifndef WOLFSSL_SP_NO_256
    if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SECP256R1) {
    #ifndef WOLFSSL_ECDSA_SET_K
        return sp_ecc_sign_256(in, inlen, rng, &key->k, r, s, NULL, key->heap);
    #else
        return sp_ecc_sign_256(in, inlen, rng, &key->k, r, s, key->sign_k,
                                                                     key->heap);
    #endif
    }
#endif
#ifdef WOLFSSL_SP_384
    if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == ECC_SECP384R1) {
    #ifndef WOLFSSL_ECDSA_SET_K
        return sp_ecc_sign_384(in, inlen, rng, &key->k, r, s, NULL, key->heap);
    #else
        return sp_ecc_sign_384(in, inlen, rng, &key->k, r, s, key->sign_k,
                                                                     key->heap);
    #endif
    }
#endif
    return WC_KEY_SIZE_E;
#else
#ifdef WOLFSSL_HAVE_SP_ECC
    #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC)
    if (key->asyncDev.marker != WOLFSSL_ASYNC_MARKER_ECC)
    #endif
    {
#ifndef WOLFSSL_SP_NO_256
        if (key->idx != ECC_CUSTOM_IDX &&
                                       ecc_sets[key->idx].id == ECC_SECP256R1) {
        #ifndef WOLFSSL_ECDSA_SET_K
            return sp_ecc_sign_256(in, inlen, rng, &key->k, r, s, NULL,
                                                                     key->heap);
        #else
            return sp_ecc_sign_256(in, inlen, rng, &key->k, r, s, key->sign_k,
                                                                     key->heap);
        #endif
        }
#endif
#ifdef WOLFSSL_SP_384
        if (key->idx != ECC_CUSTOM_IDX &&
                                       ecc_sets[key->idx].id == ECC_SECP384R1) {
        #ifndef WOLFSSL_ECDSA_SET_K
            return sp_ecc_sign_384(in, inlen, rng, &key->k, r, s, NULL,
                                                                     key->heap);
        #else
            return sp_ecc_sign_384(in, inlen, rng, &key->k, r, s, key->sign_k,
                                                                     key->heap);
        #endif
        }
#endif
    }
#endif /* WOLFSSL_HAVE_SP_ECC */


#if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC) && \
       defined(WOLFSSL_ASYNC_CRYPT_TEST)
    if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_ECC) {
        if (wc_AsyncTestInit(&key->asyncDev, ASYNC_TEST_ECC_SIGN)) {
            WC_ASYNC_TEST* testDev = &key->asyncDev.test;
            testDev->eccSign.in = in;
            testDev->eccSign.inSz = inlen;
            testDev->eccSign.rng = rng;
            testDev->eccSign.key = key;
            testDev->eccSign.r = r;
            testDev->eccSign.s = s;
            return WC_PENDING_E;
        }
    }
#endif

#if defined(WOLFSSL_ASYNC_CRYPT) && defined(HAVE_CAVIUM_V)
   err = wc_ecc_alloc_mpint(key, &key->e);
   if (err != 0) {
      return err;
   }
   e = key->e;
#elif !defined(WOLFSSL_SMALL_STACK)
   e = &e_lcl;
#else
   e = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_ECC);
   if (e == NULL) {
      return MEMORY_E;
   }
#endif

   /* get the hash and load it as a bignum into 'e' */
   /* init the bignums */
   if ((err = mp_init(e)) != MP_OKAY) {
   #ifdef WOLFSSL_SMALL_STACK
      XFREE(e, key->heap, DYNAMIC_TYPE_ECC);
   #endif
      return err;
   }

   /* load curve info */
#if defined(WOLFSSL_ECDSA_SET_K)
   ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
   err = wc_ecc_curve_load(key->dp, &curve, ECC_CURVE_FIELD_ALL);
#else
   #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC) && \
      (defined(HAVE_CAVIUM_V) || defined(HAVE_INTEL_QA))
   if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_ECC) {
      ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
      err = wc_ecc_curve_load(key->dp, &curve, ECC_CURVE_FIELD_ALL);
   }
   else
   #endif
   {
      ALLOC_CURVE_SPECS(1);
      err = wc_ecc_curve_load(key->dp, &curve, ECC_CURVE_FIELD_ORDER);
   }
#endif

   /* load digest into e */
   if (err == MP_OKAY) {
       /* we may need to truncate if hash is longer than key size */
       word32 orderBits = mp_count_bits(curve->order);

       /* truncate down to byte size, may be all that's needed */
       if ((WOLFSSL_BIT_SIZE * inlen) > orderBits)
           inlen = (orderBits + WOLFSSL_BIT_SIZE - 1) / WOLFSSL_BIT_SIZE;
       err = mp_read_unsigned_bin(e, (byte*)in, inlen);

       /* may still need bit truncation too */
       if (err == MP_OKAY && (WOLFSSL_BIT_SIZE * inlen) > orderBits)
           mp_rshb(e, WOLFSSL_BIT_SIZE - (orderBits & 0x7));
   }

   /* make up a key and export the public copy */
   if (err == MP_OKAY) {
       int      loop_check = 0;
   #ifdef WOLFSSL_SMALL_STACK
       ecc_key* pubkey;
   #else
       ecc_key  pubkey[1];
   #endif

   #if defined(WOLFSSL_ASYNC_CRYPT) && defined(WC_ASYNC_ENABLE_ECC)
        if (key->asyncDev.marker == WOLFSSL_ASYNC_MARKER_ECC) {
        #if defined(HAVE_CAVIUM_V) || defined(HAVE_INTEL_QA)
        #ifdef HAVE_CAVIUM_V
            if (NitroxEccIsCurveSupported(key))
        #endif
            {
               word32 keySz = key->dp->size;
               mp_int* k;
            #ifdef HAVE_CAVIUM_V
               err = wc_ecc_alloc_mpint(key, &key->signK);
               if (err != 0)
                  return err;
               k = key->signK;
            #else
               mp_int k_lcl;
               k = &k_lcl;
            #endif

               err = mp_init(k);

                /* make sure r and s are allocated */
           #ifdef HAVE_CAVIUM_V
               /* Nitrox V needs single buffer for R and S */
               if (err == MP_OKAY)
                   err = wc_bigint_alloc(&key->r->raw, NitroxEccGetSize(key)*2);
               /* Nitrox V only needs Prime and Order */
               if (err == MP_OKAY)
                   err = wc_ecc_curve_load(key->dp, &curve,
                        (ECC_CURVE_FIELD_PRIME | ECC_CURVE_FIELD_ORDER));
           #else
               if (err == MP_OKAY)
                   err = wc_bigint_alloc(&key->r->raw, key->dp->size);
               if (err == MP_OKAY)
                   err = wc_ecc_curve_load(key->dp, &curve, ECC_CURVE_FIELD_ALL);
           #endif
               if (err == MP_OKAY)
                   err = wc_bigint_alloc(&key->s->raw, key->dp->size);

               /* load e and k */
               if (err == MP_OKAY)
                   err = wc_mp_to_bigint_sz(e, &e->raw, keySz);
               if (err == MP_OKAY)
                   err = wc_mp_to_bigint_sz(&key->k, &key->k.raw, keySz);
               if (err == MP_OKAY)
                   err = wc_ecc_gen_k(rng, key->dp->size, k, curve->order);
               if (err == MP_OKAY)
                   err = wc_mp_to_bigint_sz(k, &k->raw, keySz);

           #ifdef HAVE_CAVIUM_V
               if (err == MP_OKAY)
                   err = NitroxEcdsaSign(key, &e->raw, &key->k.raw, &k->raw,
                    &r->raw, &s->raw, &curve->prime->raw, &curve->order->raw);
           #else
               if (err == MP_OKAY)
                   err = IntelQaEcdsaSign(&key->asyncDev, &e->raw, &key->k.raw,
                      &k->raw, &r->raw, &s->raw, &curve->Af->raw, &curve->Bf->raw,
                      &curve->prime->raw, &curve->order->raw, &curve->Gx->raw,
                      &curve->Gy->raw);
           #endif

           #ifndef HAVE_CAVIUM_V
               mp_clear(e);
               mp_clear(k);
           #endif
               wc_ecc_curve_free(curve);
               FREE_CURVE_SPECS();

               return err;
           }
       #endif /* HAVE_CAVIUM_V || HAVE_INTEL_QA */
       }
   #endif /* WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_ECC */

   #ifdef WOLFSSL_SMALL_STACK
       pubkey = (ecc_key*)XMALLOC(sizeof(ecc_key), key->heap, DYNAMIC_TYPE_ECC);
       if (pubkey == NULL)
           err = MEMORY_E;
   #endif

       /* don't use async for key, since we don't support async return here */
       if (err == MP_OKAY && (err = wc_ecc_init_ex(pubkey, key->heap,
                                                   INVALID_DEVID)) == MP_OKAY) {
       #ifdef WOLFSSL_SMALL_STACK
           mp_int* b = NULL;
       #else
           mp_int  b[1];
       #endif

       #ifdef WOLFSSL_SMALL_STACK
           if (err == MP_OKAY) {
               b = (mp_int*)XMALLOC(sizeof(mp_int), key->heap,
                                                              DYNAMIC_TYPE_ECC);
               if (b == NULL)
                   err = MEMORY_E;
           }
       #endif

           if (err == MP_OKAY) {
               err = mp_init(b);
           }

       #ifdef WOLFSSL_CUSTOM_CURVES
           /* if custom curve, apply params to pubkey */
           if (err == MP_OKAY && key->idx == ECC_CUSTOM_IDX) {
               err = wc_ecc_set_custom_curve(pubkey, key->dp);
           }
       #endif

           if (err == MP_OKAY) {
               /* Generate blinding value - non-zero value. */
               do {
                   if (++loop_check > 64) {
                        err = RNG_FAILURE_E;
                        break;
                   }

                   err = wc_ecc_gen_k(rng, key->dp->size, b, curve->order);
               }
               while (err == MP_ZERO_E);
               loop_check = 0;
           }

           for (; err == MP_OKAY;) {
               if (++loop_check > 64) {
                    err = RNG_FAILURE_E;
                    break;
               }
       #ifdef WOLFSSL_ECDSA_SET_K
               if (key->sign_k != NULL) {
                   if (loop_check > 1) {
                      err = RNG_FAILURE_E;
                      break;
                   }

                   err = mp_copy(key->sign_k, &pubkey->k);
                   if (err != MP_OKAY) break;

                   mp_forcezero(key->sign_k);
                   mp_free(key->sign_k);
                   XFREE(key->sign_k, key->heap, DYNAMIC_TYPE_ECC);
                   key->sign_k = NULL;
                   err = wc_ecc_make_pub_ex(pubkey, curve, NULL);
               }
               else
       #endif
               {
                   err = wc_ecc_make_key_ex(rng, key->dp->size, pubkey,
                                                                   key->dp->id);
               }
               if (err != MP_OKAY) break;

               /* find r = x1 mod n */
               err = mp_mod(pubkey->pubkey.x, curve->order, r);
               if (err != MP_OKAY) break;

               if (mp_iszero(r) == MP_YES) {
                #ifndef ALT_ECC_SIZE
                   mp_clear(pubkey->pubkey.x);
                   mp_clear(pubkey->pubkey.y);
                   mp_clear(pubkey->pubkey.z);
                #endif
                   mp_forcezero(&pubkey->k);
               }
               else {
                   /* find s = (e + xr)/k
                             = b.(e/k.b + x.r/k.b) */

                   /* k = k.b */
                   err = mp_mulmod(&pubkey->k, b, curve->order, &pubkey->k);
                   if (err != MP_OKAY) break;

                   /* k = 1/k.b */
                   err = mp_invmod(&pubkey->k, curve->order, &pubkey->k);
                   if (err != MP_OKAY) break;

                   /* s = x.r */
                   err = mp_mulmod(&key->k, r, curve->order, s);
                   if (err != MP_OKAY) break;

                   /* s = x.r/k.b */
                   err = mp_mulmod(&pubkey->k, s, curve->order, s);
                   if (err != MP_OKAY) break;

                   /* e = e/k.b */
                   err = mp_mulmod(&pubkey->k, e, curve->order, e);
                   if (err != MP_OKAY) break;

                   /* s = e/k.b + x.r/k.b
                        = (e + x.r)/k.b */
                   err = mp_add(e, s, s);
                   if (err != MP_OKAY) break;

                   /* s = b.(e + x.r)/k.b
                        = (e + x.r)/k */
                   err = mp_mulmod(s, b, curve->order, s);
                   if (err != MP_OKAY) break;

                   /* s = (e + xr)/k */
                   err = mp_mod(s, curve->order, s);
                   if (err != MP_OKAY) break;

                   if (mp_iszero(s) == MP_NO)
                       break;
                }
           }
           mp_clear(b);
           mp_free(b);
       #ifdef WOLFSSL_SMALL_STACK
           XFREE(b, key->heap, DYNAMIC_TYPE_ECC);
       #endif
           wc_ecc_free(pubkey);
       #ifdef WOLFSSL_SMALL_STACK
           XFREE(pubkey, key->heap, DYNAMIC_TYPE_ECC);
       #endif
       }
   }

   mp_clear(e);
   wc_ecc_curve_free(curve);
#ifdef WOLFSSL_SMALL_STACK
   XFREE(e, key->heap, DYNAMIC_TYPE_ECC);
#endif
   FREE_CURVE_SPECS();
#endif /* WOLFSSL_SP_MATH */

   return err;
}