From: Andrew Golovashevich Date: Sun, 5 May 2024 11:27:26 +0000 (+0300) Subject: Optimizated calculation of shared power of 2 in bn_gcd X-Git-Tag: openssl-3.4.0-alpha1~543 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=aaa1bda7187c8d920cf9e426c2cf8ec7c1c65576;p=thirdparty%2Fopenssl.git Optimizated calculation of shared power of 2 in bn_gcd Reviewed-by: Paul Dale Reviewed-by: Bernd Edlinger Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/24332) --- diff --git a/crypto/bn/bn_gcd.c b/crypto/bn/bn_gcd.c index 2cd8ee35e03..fd6cf9d8f73 100644 --- a/crypto/bn/bn_gcd.c +++ b/crypto/bn/bn_gcd.c @@ -9,6 +9,7 @@ #include "internal/cryptlib.h" #include "bn_local.h" +#include "internal/constant_time.h" /* * bn_mod_inverse_no_branch is a special version of BN_mod_inverse. It does @@ -580,8 +581,8 @@ end: int BN_gcd(BIGNUM *r, const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) { BIGNUM *g, *temp = NULL; - BN_ULONG mask = 0; - int i, j, top, rlen, glen, m, bit = 1, delta = 1, cond = 0, shifts = 0, ret = 0; + BN_ULONG pow2_numbits, pow2_numbits_temp, pow2_condition_mask, pow2_flag; + int i, j, top, rlen, glen, m, delta = 1, cond = 0, pow2_shifts, ret = 0; /* Note 2: zero input corner cases are not constant-time since they are * handled immediately. An attacker can run an attack under this @@ -611,18 +612,29 @@ int BN_gcd(BIGNUM *r, const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) goto err; /* find shared powers of two, i.e. "shifts" >= 1 */ + pow2_flag = 1; + pow2_shifts = 0; + pow2_numbits = 0; for (i = 0; i < r->dmax && i < g->dmax; i++) { - mask = ~(r->d[i] | g->d[i]); - for (j = 0; j < BN_BITS2; j++) { - bit &= mask; - shifts += bit; - mask >>= 1; - } + pow2_numbits_temp = r->d[i] | g->d[i]; + pow2_condition_mask = constant_time_is_zero_bn(pow2_flag); + pow2_flag &= constant_time_is_zero_bn(pow2_numbits_temp); + pow2_shifts += pow2_flag; + pow2_numbits = constant_time_select_bn(pow2_condition_mask, + pow2_numbits, pow2_numbits_temp); + } + pow2_numbits = ~pow2_numbits; + pow2_shifts *= BN_BITS2; + pow2_flag = 1; + for (j = 0; j < BN_BITS2; j++) { + pow2_flag &= pow2_numbits; + pow2_shifts += pow2_flag; + pow2_numbits >>= 1; } /* subtract shared powers of two; shifts >= 1 */ - if (!BN_rshift(r, r, shifts) - || !BN_rshift(g, g, shifts)) + if (!BN_rshift(r, r, pow2_shifts) + || !BN_rshift(g, g, pow2_shifts)) goto err; /* expand to biggest nword, with room for a possible extra word */ @@ -665,7 +677,7 @@ int BN_gcd(BIGNUM *r, const BIGNUM *in_a, const BIGNUM *in_b, BN_CTX *ctx) /* remove possible negative sign */ r->neg = 0; /* add powers of 2 removed, then correct the artificial shift */ - if (!BN_lshift(r, r, shifts) + if (!BN_lshift(r, r, pow2_shifts) || !BN_rshift1(r, r)) goto err; diff --git a/include/internal/constant_time.h b/include/internal/constant_time.h index f2572ded519..9ffa4399a35 100644 --- a/include/internal/constant_time.h +++ b/include/internal/constant_time.h @@ -141,6 +141,17 @@ static ossl_inline uint64_t constant_time_lt_64(uint64_t a, uint64_t b) } #ifdef BN_ULONG +static ossl_inline BN_ULONG value_barrier_bn(BN_ULONG a) +{ +#if !defined(OPENSSL_NO_ASM) && defined(__GNUC__) + BN_ULONG r; + __asm__("" : "=r"(r) : "0"(a)); +#else + volatile BN_ULONG r = a; +#endif + return r; +} + static ossl_inline BN_ULONG constant_time_msb_bn(BN_ULONG a) { return 0 - (a >> (sizeof(a) * 8 - 1)); @@ -161,6 +172,13 @@ static ossl_inline BN_ULONG constant_time_eq_bn(BN_ULONG a, { return constant_time_is_zero_bn(a ^ b); } + +static ossl_inline BN_ULONG constant_time_select_bn(BN_ULONG mask, + BN_ULONG a, + BN_ULONG b) +{ + return (value_barrier_bn(mask) & a) | (value_barrier_bn(~mask) & b); +} #endif static ossl_inline unsigned int constant_time_ge(unsigned int a,