CVE-2018-12436: Exposure of Sensitive Information to an Unauthorized Actor (CWE-200)
value: wolfcrypt/src/ecc.c in wolfSSL before 3.15.1.patch allows a memory-cache side-channel attack on ECDSA signatures, aka the Return Of the Hidden Number Problem or ROHNP. To discover an ECDSA key, the attacker needs access to either the local machine or a different virtual machine on the same physical host.
CWE description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
CVSS v3 base score: 4.7
Published: 2018-06-15T02:29Z
Repository: https://github.com/wolfSSL/wolfssl
Language: C
Files changed: wolfcrypt/src/ecc.c
Commit date: 2018-06-06 11:52:04 +1000
Commit message: Change ECDSA signing to use blinding.

Diff: diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c
index ee271da8..6bfd2105 100644
--- a/wolfcrypt/src/ecc.c
+++ b/wolfcrypt/src/ecc.c
@@ -3137,23 +3137,23 @@ static int wc_ecc_gen_k(WC_RNG* rng, int size, mp_int* k, mp_int* order)
 
     /* load random buffer data into k */
     if (err == 0)
         err = mp_read_unsigned_bin(k, (byte*)buf, size);
 
-    /* quick sanity check to make sure we're not dealing with a 0 key */
-    if (err == MP_OKAY) {
-        if (mp_iszero(k) == MP_YES)
-          err = MP_ZERO_E;
-    }
-
     /* the key should be smaller than the order of base point */
     if (err == MP_OKAY) {
         if (mp_cmp(k, order) != MP_LT) {
             err = mp_mod(k, order, k);
         }
     }
 
+    /* quick sanity check to make sure we're not dealing with a 0 key */
+    if (err == MP_OKAY) {
+        if (mp_iszero(k) == MP_YES)
+          err = MP_ZERO_E;
+    }
+
     ForceZero(buf, ECC_MAXSIZE);
 #ifdef WOLFSSL_SMALL_STACK
     XFREE(buf, NULL, DYNAMIC_TYPE_ECC_BUFFER);
 #endif
 
@@ -3922,24 +3922,44 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng,
        }
    #endif /* WOLFSSL_ASYNC_CRYPT */
 
        /* don't use async for key, since we don't support async return here */
        if ((err = wc_ecc_init_ex(&pubkey, key->heap, INVALID_DEVID)) == MP_OKAY) {
+           mp_int b;
+
+           if (err == MP_OKAY) {
+               err = mp_init(&b);
+           }
+
        #ifdef WOLFSSL_CUSTOM_CURVES
            /* if custom curve, apply params to pubkey */
-           if (key->idx == ECC_CUSTOM_IDX) {
+           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;
                }
                err = wc_ecc_make_key_ex(rng, key->dp->size, &pubkey,
-                                                              key->dp->id);
+                                                                   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;
@@ -3951,34 +3971,54 @@ int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng,
                    mp_clear(pubkey.pubkey.z);
                 #endif
                    mp_forcezero(&pubkey.k);
                }
                else {
-                   /* find s = (e + xr)/k */
+                   /* 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 = xr */
+                   /* s = x.r */
                    err = mp_mulmod(&key->k, r, curve->order, s);
                    if (err != MP_OKAY) break;
 
-                   /* s = e +  xr */
+                   /* 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 = e +  xr */
-                   err = mp_mod(s, curve->order, s);
+                   /* 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_mulmod(s, &pubkey.k, curve->order, s);
+                   err = mp_mod(s, curve->order, s);
+                   if (err != MP_OKAY) break;
 
                    if (mp_iszero(s) == MP_NO)
                        break;
                 }
            }
            wc_ecc_free(&pubkey);
+           mp_clear(&b);
+           mp_free(&b);
        }
    }
 
    mp_clear(e);
    wc_ecc_curve_free(curve);

Vulnerable code:     /* quick sanity check to make sure we're not dealing with a 0 key */
    if (err == MP_OKAY) {
        if (mp_iszero(k) == MP_YES)
          err = MP_ZERO_E;
    }

           if (key->idx == ECC_CUSTOM_IDX) {
                                                              key->dp->id);
                   /* find s = (e + xr)/k */
                   /* s = xr */
                   /* s = e +  xr */
                   /* s = e +  xr */
                   err = mp_mod(s, curve->order, s);
                   err = mp_mulmod(s, &pubkey.k, curve->order, s);
Fixed code:     /* quick sanity check to make sure we're not dealing with a 0 key */
    if (err == MP_OKAY) {
        if (mp_iszero(k) == MP_YES)
          err = MP_ZERO_E;
    }

           mp_int b;

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

           if (err == MP_OKAY && key->idx == ECC_CUSTOM_IDX) {
           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;
           }

                                                                   key->dp->id);
                   /* 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 */
                   /* s = x.r */
                   /* 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 */
                   /* s = b.(e + x.r)/k.b
                        = (e + x.r)/k */
                   err = mp_mulmod(s, &b, curve->order, s);
                   err = mp_mod(s, curve->order, s);
                   if (err != MP_OKAY) break;
           mp_clear(&b);
           mp_free(&b);