ecdsa/ecs_ossl.c: revert blinding in ECDSA signature.
authorAndy Polyakov <appro@openssl.org>
Fri, 6 Jul 2018 13:55:34 +0000 (15:55 +0200)
committerAndy Polyakov <appro@openssl.org>
Wed, 1 Aug 2018 14:15:17 +0000 (16:15 +0200)
Originally suggested solution for "Return Of the Hidden Number Problem"
is arguably too expensive. While it has marginal impact on slower
curves, none to ~6%, optimized implementations suffer real penalties.
Most notably sign with P-256 went more than 2 times[!] slower. Instead,
just implement constant-time BN_mod_add_quick.

Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/6810)

(cherry picked from commit 3fc7a9b96cbed0c3da6f53c08e34d8d0c982745f)

Resolved onflicts:
crypto/ec/ecdsa_ossl.c
crypto/include/internal/bn_int.h

crypto/bn/bn_mod.c
crypto/bn_int.h
crypto/ecdsa/ecs_ossl.c

index ffbce890cf288b819622340fc363657b3b0a71cb..df6f1e2452e3c56b8d9cf2e659fd94e64e359100 100644 (file)
@@ -149,18 +149,72 @@ int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
 
 /*
  * BN_mod_add variant that may be used if both a and b are non-negative and
- * less than m
+ * less than m. The original algorithm was
+ *
+ *    if (!BN_uadd(r, a, b))
+ *       return 0;
+ *    if (BN_ucmp(r, m) >= 0)
+ *       return BN_usub(r, r, m);
+ *
+ * which is replaced with addition, subtracting modulus, and conditional
+ * move depending on whether or not subtraction borrowed.
  */
-int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
-                     const BIGNUM *m)
+int bn_mod_add_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                         const BIGNUM *m)
 {
-    if (!BN_uadd(r, a, b))
+    size_t i, ai, bi, mtop = m->top;
+    BN_ULONG storage[1024 / BN_BITS2];
+    BN_ULONG carry, temp, mask, *rp, *tp = storage;
+    const BN_ULONG *ap, *bp;
+
+    if (bn_wexpand(r, m->top) == NULL)
         return 0;
-    if (BN_ucmp(r, m) >= 0)
-        return BN_usub(r, r, m);
+
+    if (mtop > sizeof(storage) / sizeof(storage[0])
+        && (tp = OPENSSL_malloc(mtop * sizeof(BN_ULONG))) == NULL)
+       return 0;
+
+    ap = a->d != NULL ? a->d : tp;
+    bp = b->d != NULL ? b->d : tp;
+
+    for (i = 0, ai = 0, bi = 0, carry = 0; i < mtop;) {
+        mask = (BN_ULONG)0 - ((i - a->top) >> (8 * sizeof(i) - 1));
+        temp = ((ap[ai] & mask) + carry) & BN_MASK2;
+        carry = (temp < carry);
+
+        mask = (BN_ULONG)0 - ((i - b->top) >> (8 * sizeof(i) - 1));
+        tp[i] = ((bp[bi] & mask) + temp) & BN_MASK2;
+        carry += (tp[i] < temp);
+
+        i++;
+        ai += (i - a->dmax) >> (8 * sizeof(i) - 1);
+        bi += (i - b->dmax) >> (8 * sizeof(i) - 1);
+    }
+    rp = r->d;
+    carry -= bn_sub_words(rp, tp, m->d, mtop);
+    for (i = 0; i < mtop; i++) {
+        rp[i] = (carry & tp[i]) | (~carry & rp[i]);
+        ((volatile BN_ULONG *)tp)[i] = 0;
+    }
+    r->top = mtop;
+
+    if (tp != storage)
+        OPENSSL_free(tp);
+
     return 1;
 }
 
+int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                     const BIGNUM *m)
+{
+    int ret = bn_mod_add_fixed_top(r, a, b, m);
+
+    if (ret)
+        bn_correct_top(r);
+
+    return ret;
+}
+
 int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m,
                BN_CTX *ctx)
 {
index a3b0fa7960c0ba195462d99422f26acfb22f552e..9683e5f60c7ae02a17a5542d0ce397ea8843dcfe 100644 (file)
@@ -9,3 +9,5 @@ int bn_mul_mont_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
                           BN_MONT_CTX *mont, BN_CTX *ctx);
 int bn_to_mont_fixed_top(BIGNUM *r, const BIGNUM *a, BN_MONT_CTX *mont,
                          BN_CTX *ctx);
+int bn_mod_add_fixed_top(BIGNUM *r, const BIGNUM *a, const BIGNUM *b,
+                         const BIGNUM *m);
index 6115df74079493ce0bfa507da0b3220641ac6e31..16d4f59b9ba97c44082e1a51c8240aee720c9f4f 100644 (file)
@@ -252,7 +252,6 @@ static ECDSA_SIG *ecdsa_do_sign(const unsigned char *dgst, int dgst_len,
 {
     int ok = 0, i;
     BIGNUM *kinv = NULL, *s, *m = NULL, *tmp = NULL, *order = NULL;
-    BIGNUM *blind = NULL, *blindm = NULL;
     const BIGNUM *ckinv;
     BN_CTX *ctx = NULL;
     const EC_GROUP *group;
@@ -270,25 +269,14 @@ static ECDSA_SIG *ecdsa_do_sign(const unsigned char *dgst, int dgst_len,
     }
 
     ret = ECDSA_SIG_new();
-    if (ret == NULL) {
+    if (!ret) {
         ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
         return NULL;
     }
     s = ret->s;
 
-    ctx = BN_CTX_new();
-    if (ctx == NULL) {
-        ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
-        goto err;
-    }
-
-    BN_CTX_start(ctx);
-    order = BN_CTX_get(ctx);
-    tmp = BN_CTX_get(ctx);
-    m = BN_CTX_get(ctx);
-    blind = BN_CTX_get(ctx);
-    blindm = BN_CTX_get(ctx);
-    if (blindm == NULL) {
+    if ((ctx = BN_CTX_new()) == NULL || (order = BN_new()) == NULL ||
+        (tmp = BN_new()) == NULL || (m = BN_new()) == NULL) {
         ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_MALLOC_FAILURE);
         goto err;
     }
@@ -327,70 +315,26 @@ static ECDSA_SIG *ecdsa_do_sign(const unsigned char *dgst, int dgst_len,
             }
         }
 
-        /*
-         * The normal signature calculation is:
-         *
-         *   s := k^-1 * (m + r * priv_key) mod order
-         *
-         * We will blind this to protect against side channel attacks
-         *
-         *   s := blind^-1 * k^-1 * (blind * m + blind * r * priv_key) mod order
-         */
-
-        /* Generate a blinding value */
-        do {
-            if (!BN_rand(blind, BN_num_bits(order) - 1, -1, 0))
-                goto err;
-        } while (BN_is_zero(blind));
-        BN_set_flags(blind, BN_FLG_CONSTTIME);
-        BN_set_flags(blindm, BN_FLG_CONSTTIME);
-        BN_set_flags(tmp, BN_FLG_CONSTTIME);
-
-        /* tmp := blind * priv_key * r mod order */
-        if (!BN_mod_mul(tmp, blind, priv_key, order, ctx)) {
-            ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
-            goto err;
-        }
-        if (!BN_mod_mul(tmp, tmp, ret->r, order, ctx)) {
-            ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
-            goto err;
-        }
-
-        /* blindm := blind * m mod order */
-        if (!BN_mod_mul(blindm, blind, m, order, ctx)) {
+        if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx)) {
             ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
             goto err;
         }
-
-        /* s : = (blind * priv_key * r) + (blind * m) mod order */
-        if (!BN_mod_add_quick(s, tmp, blindm, order)) {
+        if (!BN_mod_add_quick(s, tmp, m, order)) {
             ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
             goto err;
         }
-
-        /* s := s * k^-1 mod order */
         if (!BN_mod_mul(s, s, ckinv, order, ctx)) {
             ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
             goto err;
         }
-
-        /* s:= s * blind^-1 mod order */
-        if (BN_mod_inverse(blind, blind, order, ctx) == NULL) {
-            ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
-            goto err;
-        }
-        if (!BN_mod_mul(s, s, blind, order, ctx)) {
-            ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ERR_R_BN_LIB);
-            goto err;
-        }
-
         if (BN_is_zero(s)) {
             /*
              * if kinv and r have been supplied by the caller don't to
              * generate new kinv and r values
              */
             if (in_kinv != NULL && in_r != NULL) {
-                ECDSAerr(ECDSA_F_ECDSA_DO_SIGN, ECDSA_R_NEED_NEW_SETUP_VALUES);
+                ECDSAerr(ECDSA_F_ECDSA_DO_SIGN,
+                         ECDSA_R_NEED_NEW_SETUP_VALUES);
                 goto err;
             }
         } else
@@ -405,11 +349,15 @@ static ECDSA_SIG *ecdsa_do_sign(const unsigned char *dgst, int dgst_len,
         ECDSA_SIG_free(ret);
         ret = NULL;
     }
-    if (ctx != NULL) {
-        BN_CTX_end(ctx);
+    if (ctx)
         BN_CTX_free(ctx);
-    }
-    if (kinv != NULL)
+    if (m)
+        BN_clear_free(m);
+    if (tmp)
+        BN_clear_free(tmp);
+    if (order)
+        BN_free(order);
+    if (kinv)
         BN_clear_free(kinv);
     return ret;
 }