From 0548c882ef8a8113cb38159f8a98011a61a252d8 Mon Sep 17 00:00:00 2001 From: "Diego F. Aranha" Date: Wed, 18 Mar 2020 15:57:00 +0100 Subject: [PATCH] Implement blinding for EC scalar multiplication This commit implements coordinate blinding for the generic implementations of both binary and prime elliptic curves in 1.0.2, to avoid leaking bits of the scalar and, potentially, bug attacks. While blinding is implemented in the 1.1.1 and master branches, it was deliberately decided to avoid backporting those changes as they were originally written for the newer branches, as the solution adopted there required major restructuring of code and structures that was deemed not suitable for 1.0.2. A group of security researchers and cryptographers from academia and industry, listed below, reported a successful cache timing attack in OpenSSL 1.0.2u against specific prime and binary curves whose order or field length is close to a word boundary. In this commit, as a possible fix, the authors propose implementing coordinate randomization to balance the two possibilities for the key bit in the first loop iteration of the Montgomery ladder. This way, the Z coordinates of both accumulator points will be non-trivial and the multiplication latency will be similar, with a tiny performance penalty. The original GitHub Pull Request #11361 includes more details about the reported attack, literature references and discussions on how the originally proposed fix was incrementally edited to reflect the relevant details of the 1.1.1 and master branches regarding coordinate blinding. The authors of the original report and fix are Diego F. Aranha and Akira Takahashi (both from Aarhus University), Mehdi Tibouchi (NTT Corporation) and Yuval Yarom (University of Adelaide). Co-authored-by: Akira Takahashi Co-authored-by: Mehdi Tibouchi Co-authored-by: Yuval Yarom --- crypto/ec/ec.h | 1 + crypto/ec/ec2_mult.c | 34 ++++++++++++++++--- crypto/ec/ec_err.c | 3 +- crypto/ec/ec_mult.c | 77 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 107 insertions(+), 8 deletions(-) diff --git a/crypto/ec/ec.h b/crypto/ec/ec.h index 012703666e381..2f579190fba8f 100644 --- a/crypto/ec/ec.h +++ b/crypto/ec/ec.h @@ -1182,6 +1182,7 @@ void ERR_load_EC_strings(void); # define EC_F_EC_KEY_PRINT 180 # define EC_F_EC_KEY_PRINT_FP 181 # define EC_F_EC_KEY_SET_PUBLIC_KEY_AFFINE_COORDINATES 229 +# define EC_F_EC_MUL_CONSTTIME 246 # define EC_F_EC_POINTS_MAKE_AFFINE 136 # define EC_F_EC_POINT_ADD 112 # define EC_F_EC_POINT_CMP 113 diff --git a/crypto/ec/ec2_mult.c b/crypto/ec/ec2_mult.c index 1f9cc00aead6c..0f8aea8826321 100644 --- a/crypto/ec/ec2_mult.c +++ b/crypto/ec/ec2_mult.c @@ -315,6 +315,34 @@ static int ec_GF2m_montgomery_point_multiply(const EC_GROUP *group, if (!BN_GF2m_add(x2, x2, &group->b)) goto err; /* x2 = x^4 + b */ + /* blinding: make sure z1 and z2 are independently blinded. */ + do { + if (!BN_rand(z1, BN_num_bits(&group->field) - 1, -1, 0)) { + ECerr(EC_F_EC_GF2M_MONTGOMERY_POINT_MULTIPLY, ERR_R_BN_LIB); + return 0; + } + } while (BN_is_zero(z1)); + + /* first blind (x2,z2) using z1 as the random field element. */ + if ((group->meth->field_encode != NULL + && !group->meth->field_encode(group, z1, z1, ctx)) + || !group->meth->field_mul(group, x2, x2, z1, ctx) + || !group->meth->field_mul(group, z2, z2, z1, ctx)) + return 0; + + /* now generate another random field element to blind (x1,z1) */ + do { + if (!BN_rand(z1, BN_num_bits(&group->field) - 1, -1, 0)) { + ECerr(EC_F_EC_GF2M_MONTGOMERY_POINT_MULTIPLY, ERR_R_BN_LIB); + return 0; + } + } while (BN_is_zero(z1)); + + if ((group->meth->field_encode != NULL + && !group->meth->field_encode(group, z1, z1, ctx)) + || !group->meth->field_mul(group, x1, x1, z1, ctx)) + return 0; + /* find top most bit and go one past it */ i = scalar->top - 1; mask = BN_TBIT; @@ -393,11 +421,9 @@ int ec_GF2m_simple_mul(const EC_GROUP *group, EC_POINT *r, /* * This implementation is more efficient than the wNAF implementation for * 2 or fewer points. Use the ec_wNAF_mul implementation for 3 or more - * points, or if we can perform a fast multiplication based on - * precomputation. + * points. */ - if ((scalar && (num > 1)) || (num > 2) - || (num == 0 && EC_GROUP_have_precompute_mult(group))) { + if ((scalar && (num > 1)) || (num > 2)) { ret = ec_wNAF_mul(group, r, scalar, num, points, scalars, ctx); goto err; } diff --git a/crypto/ec/ec_err.c b/crypto/ec/ec_err.c index 220541161eb4e..8032c59403264 100644 --- a/crypto/ec/ec_err.c +++ b/crypto/ec/ec_err.c @@ -1,6 +1,6 @@ /* crypto/ec/ec_err.c */ /* ==================================================================== - * Copyright (c) 1999-2019 The OpenSSL Project. All rights reserved. + * Copyright (c) 1999-2020 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -205,6 +205,7 @@ static ERR_STRING_DATA EC_str_functs[] = { {ERR_FUNC(EC_F_EC_KEY_PRINT_FP), "EC_KEY_print_fp"}, {ERR_FUNC(EC_F_EC_KEY_SET_PUBLIC_KEY_AFFINE_COORDINATES), "EC_KEY_set_public_key_affine_coordinates"}, + {ERR_FUNC(EC_F_EC_MUL_CONSTTIME), "EC_MUL_CONSTTIME"}, {ERR_FUNC(EC_F_EC_POINTS_MAKE_AFFINE), "EC_POINTs_make_affine"}, {ERR_FUNC(EC_F_EC_POINT_ADD), "EC_POINT_add"}, {ERR_FUNC(EC_F_EC_POINT_CMP), "EC_POINT_cmp"}, diff --git a/crypto/ec/ec_mult.c b/crypto/ec/ec_mult.c index a784a99ce91e9..587a996bf566f 100644 --- a/crypto/ec/ec_mult.c +++ b/crypto/ec/ec_mult.c @@ -421,9 +421,80 @@ static int ec_mul_consttime(const EC_GROUP *group, EC_POINT *r, || (bn_wexpand(&r->Z, group_top) == NULL)) goto err; - /* top bit is a 1, in a fixed pos */ - if (!EC_POINT_copy(r, s)) - goto err; + if (group->meth->field_type == NID_X9_62_prime_field) { + /* + * Apply blinding independently to r and s: use point r as temporary + * variable. + * + * Blinding requires using a projective representation, and later we + * implement the ladder step using EC_POINT_add() and EC_POINT_dbl(): + * this requires EC_METHODs that support projective coordinates in their + * point addition and point doubling implementations. + * + * All the built-in EC_METHODs for prime curves at the moment satisfy + * this condition, while the EC_METHOD for binary curves does not. + * + * This is why we check for a prime field to apply blinding. + */ + + /* first randomize r->Z to blind s. */ + do { + if (!BN_rand_range(&r->Z, &group->field)) { + ECerr(EC_F_EC_MUL_CONSTTIME, ERR_R_BN_LIB); + goto err; + } + } while (BN_is_zero(&r->Z)); + + /* convert r->Z to the correct field representation. */ + if (group->meth->field_encode != NULL + && !group->meth->field_encode(group, &r->Z, &r->Z, ctx)) { + goto err; + } + + /* scale s->X and s->Y by r->Z^2 and r->Z^3, respectively. */ + if (!group->meth->field_sqr(group, &r->X, &r->Z, ctx) + || !group->meth->field_mul(group, &r->Y, &r->X, &r->Z, ctx)) { + goto err; + } + if (!group->meth->field_mul(group, &s->X, &s->X, &r->X, ctx) + || !group->meth->field_mul(group, &s->Y, &s->Y, &r->Y, ctx) + || !group->meth->field_mul(group, &s->Z, &s->Z, &r->Z, ctx)) { + goto err; + } + /* mark the flag in s to full projective coordinates. */ + s->Z_is_one = 0; + + /* blinding: now rerandomize r->Z to make r a blinded copy of s. */ + do { + if (!BN_rand_range(&r->Z, &group->field)) { + ECerr(EC_F_EC_MUL_CONSTTIME, ERR_R_BN_LIB); + goto err; + } + } while (BN_is_zero(&r->Z)); + + /* convert r->Z to the correct field representation. */ + if (group->meth->field_encode != NULL + && !group->meth->field_encode(group, &r->Z, &r->Z, ctx)) { + goto err; + } + + /* scale r->X and r->Y by r->Z^2 and r->Z^3, respectively. */ + if (!group->meth->field_sqr(group, &r->X, &r->Z, ctx) + || !group->meth->field_mul(group, &r->Y, &r->X, &r->Z, ctx)) { + goto err; + } + if (!group->meth->field_mul(group, &r->X, &s->X, &r->X, ctx) + || !group->meth->field_mul(group, &r->Y, &s->Y, &r->Y, ctx) + || !group->meth->field_mul(group, &r->Z, &s->Z, &r->Z, ctx)) { + goto err; + } + /* mark the flag in s to full projective coordinates. */ + r->Z_is_one = 0; + } else { + /* top bit is a 1, in a fixed pos; binary curves are not blinded here */ + if (!EC_POINT_copy(r, s)) + goto err; + } EC_POINT_BN_set_flags(r, BN_FLG_CONSTTIME);