X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=crypto%2Fbn%2Fbn_exp.c;h=3c043c79aa120ee7e3cd6f004de7fec51bf36f42;hb=b5279593184d0c9664cd8152799d4e2038b3ae35;hp=74b1f0e89e0f015b939a452e1a5fc269fb3d379f;hpb=020fc820dc90dbbcf0d7e3f3345af9e44cf905a7;p=oweals%2Fopenssl.git diff --git a/crypto/bn/bn_exp.c b/crypto/bn/bn_exp.c index 74b1f0e89e..3c043c79aa 100644 --- a/crypto/bn/bn_exp.c +++ b/crypto/bn/bn_exp.c @@ -56,7 +56,7 @@ * [including the GNU Public Licence.] */ /* ==================================================================== - * Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved. + * Copyright (c) 1998-2005 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 @@ -110,49 +110,32 @@ */ -#include #include "cryptlib.h" #include "bn_lcl.h" +/* maximum precomputation table size for *variable* sliding windows */ #define TABLE_SIZE 32 -/* slow but works */ -int BN_mod_mul(BIGNUM *ret, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, - BN_CTX *ctx) - { - BIGNUM *t; - int r=0; - - bn_check_top(a); - bn_check_top(b); - bn_check_top(m); - - BN_CTX_start(ctx); - if ((t = BN_CTX_get(ctx)) == NULL) goto err; - if (a == b) - { if (!BN_sqr(t,a,ctx)) goto err; } - else - { if (!BN_mul(t,a,b,ctx)) goto err; } - if (!BN_mod(ret,t,m,ctx)) goto err; - r=1; -err: - BN_CTX_end(ctx); - return(r); - } - - /* this one works - simple but works */ int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) { int i,bits,ret=0; BIGNUM *v,*rr; + if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) + { + /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */ + BNerr(BN_F_BN_EXP,ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } + BN_CTX_start(ctx); if ((r == a) || (r == p)) rr = BN_CTX_get(ctx); else rr = r; - if ((v = BN_CTX_get(ctx)) == NULL) goto err; + v = BN_CTX_get(ctx); + if (rr == NULL || v == NULL) goto err; if (BN_copy(v,a) == NULL) goto err; bits=BN_num_bits(p); @@ -173,6 +156,7 @@ int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) err: if (r != rr) BN_copy(r,rr); BN_CTX_end(ctx); + bn_check_top(r); return(ret); } @@ -186,21 +170,58 @@ int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, bn_check_top(p); bn_check_top(m); + /*- + * For even modulus m = 2^k*m_odd, it might make sense to compute + * a^p mod m_odd and a^p mod 2^k separately (with Montgomery + * exponentiation for the odd part), using appropriate exponent + * reductions, and combine the results using the CRT. + * + * For now, we use Montgomery only if the modulus is odd; otherwise, + * exponentiation using the reciprocal-based quick remaindering + * algorithm is used. + * + * (Timing obtained with expspeed.c [computations a^p mod m + * where a, p, m are of the same length: 256, 512, 1024, 2048, + * 4096, 8192 bits], compared to the running time of the + * standard algorithm: + * + * BN_mod_exp_mont 33 .. 40 % [AMD K6-2, Linux, debug configuration] + * 55 .. 77 % [UltraSparc processor, but + * debug-solaris-sparcv8-gcc conf.] + * + * BN_mod_exp_recp 50 .. 70 % [AMD K6-2, Linux, debug configuration] + * 62 .. 118 % [UltraSparc, debug-solaris-sparcv8-gcc] + * + * On the Sparc, BN_mod_exp_recp was faster than BN_mod_exp_mont + * at 2048 and more bits, but at 512 and 1024 bits, it was + * slower even than the standard algorithm! + * + * "Real" timings [linux-elf, solaris-sparcv9-gcc configurations] + * should be obtained when the new Montgomery reduction code + * has been integrated into OpenSSL.) + */ + +#define MONT_MUL_MOD +#define MONT_EXP_WORD +#define RECP_MUL_MOD + #ifdef MONT_MUL_MOD /* I have finally been able to take out this pre-condition of * the top bit being set. It was caused by an error in BN_div * with negatives. There was also another problem when for a^b%m * a >= m. eay 07-May-97 */ -/* if ((m->d[m->top-1]&BN_TBIT) && BN_is_odd(m)) */ + /* if ((m->d[m->top-1]&BN_TBIT) && BN_is_odd(m)) */ if (BN_is_odd(m)) { - if (a->top == 1) +# ifdef MONT_EXP_WORD + if (a->top == 1 && !a->neg && (BN_get_flags(p, BN_FLG_CONSTTIME) == 0)) { BN_ULONG A = a->d[0]; ret=BN_mod_exp_mont_word(r,A,p,m,ctx,NULL); } else +# endif ret=BN_mod_exp_mont(r,a,p,m,ctx,NULL); } else @@ -211,6 +232,7 @@ int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, { ret=BN_mod_exp_simple(r,a,p,m,ctx); } #endif + bn_check_top(r); return(ret); } @@ -219,43 +241,66 @@ int BN_mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx) { int i,j,bits,ret=0,wstart,wend,window,wvalue; - int start=1,ts=0; + int start=1; BIGNUM *aa; - BIGNUM val[TABLE_SIZE]; + /* Table of variables obtained from 'ctx' */ + BIGNUM *val[TABLE_SIZE]; BN_RECP_CTX recp; + if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) + { + /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */ + BNerr(BN_F_BN_MOD_EXP_RECP,ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } + bits=BN_num_bits(p); if (bits == 0) { - BN_one(r); - return(1); + ret = BN_one(r); + return ret; } BN_CTX_start(ctx); - if ((aa = BN_CTX_get(ctx)) == NULL) goto err; + aa = BN_CTX_get(ctx); + val[0] = BN_CTX_get(ctx); + if(!aa || !val[0]) goto err; BN_RECP_CTX_init(&recp); - if (BN_RECP_CTX_set(&recp,m,ctx) <= 0) goto err; - - BN_init(&(val[0])); - ts=1; + if (m->neg) + { + /* ignore sign of 'm' */ + if (!BN_copy(aa, m)) goto err; + aa->neg = 0; + if (BN_RECP_CTX_set(&recp,aa,ctx) <= 0) goto err; + } + else + { + if (BN_RECP_CTX_set(&recp,m,ctx) <= 0) goto err; + } - if (!BN_mod(&(val[0]),a,m,ctx)) goto err; /* 1 */ + if (!BN_nnmod(val[0],a,m,ctx)) goto err; /* 1 */ + if (BN_is_zero(val[0])) + { + BN_zero(r); + ret = 1; + goto err; + } window = BN_window_bits_for_exponent_size(bits); if (window > 1) { - if (!BN_mod_mul_reciprocal(aa,&(val[0]),&(val[0]),&recp,ctx)) + if (!BN_mod_mul_reciprocal(aa,val[0],val[0],&recp,ctx)) goto err; /* 2 */ j=1<<(window-1); for (i=1; i>1]),&recp,ctx)) + if (!BN_mod_mul_reciprocal(r,r,val[wvalue>>1],&recp,ctx)) goto err; /* move the 'window' down further */ @@ -319,9 +364,8 @@ int BN_mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, ret=1; err: BN_CTX_end(ctx); - for (i=0; id[0] & 1)) + if (!BN_is_odd(m)) { BNerr(BN_F_BN_MOD_EXP_MONT,BN_R_CALLED_WITH_EVEN_MODULUS); return(0); @@ -348,13 +398,15 @@ int BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, bits=BN_num_bits(p); if (bits == 0) { - BN_one(rr); - return(1); + ret = BN_one(rr); + return ret; } + BN_CTX_start(ctx); d = BN_CTX_get(ctx); r = BN_CTX_get(ctx); - if (d == NULL || r == NULL) goto err; + val[0] = BN_CTX_get(ctx); + if (!d || !r || !val[0]) goto err; /* If this is not done, things will break in the montgomery * part */ @@ -367,30 +419,34 @@ int BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, if (!BN_MONT_CTX_set(mont,m,ctx)) goto err; } - BN_init(&val[0]); - ts=1; - if (BN_ucmp(a,m) >= 0) + if (a->neg || BN_ucmp(a,m) >= 0) { - if (!BN_mod(&(val[0]),a,m,ctx)) + if (!BN_nnmod(val[0],a,m,ctx)) goto err; - aa= &(val[0]); + aa= val[0]; } else aa=a; - if (!BN_to_montgomery(&(val[0]),aa,mont,ctx)) goto err; /* 1 */ + if (BN_is_zero(aa)) + { + BN_zero(rr); + ret = 1; + goto err; + } + if (!BN_to_montgomery(val[0],aa,mont,ctx)) goto err; /* 1 */ window = BN_window_bits_for_exponent_size(bits); if (window > 1) { - if (!BN_mod_mul_montgomery(d,&(val[0]),&(val[0]),mont,ctx)) goto err; /* 2 */ + if (!BN_mod_mul_montgomery(d,val[0],val[0],mont,ctx)) goto err; /* 2 */ j=1<<(window-1); for (i=1; i>1]),mont,ctx)) + if (!BN_mod_mul_montgomery(r,r,val[wvalue>>1],mont,ctx)) goto err; /* move the 'window' down further */ @@ -457,8 +513,213 @@ int BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, err: if ((in_mont == NULL) && (mont != NULL)) BN_MONT_CTX_free(mont); BN_CTX_end(ctx); - for (i=0; itop < top) + { + b->d[b->top++] = 0; + } + + for (i = 0, j=idx; i < top * sizeof b->d[0]; i++, j+=width) + { + buf[j] = ((unsigned char*)b->d)[i]; + } + + bn_correct_top(b); + return 1; + } + +static int MOD_EXP_CTIME_COPY_FROM_PREBUF(BIGNUM *b, int top, unsigned char *buf, int idx, int width) + { + size_t i, j; + + if (bn_wexpand(b, top) == NULL) + return 0; + + for (i=0, j=idx; i < top * sizeof b->d[0]; i++, j+=width) + { + ((unsigned char*)b->d)[i] = buf[j]; + } + + b->top = top; + bn_correct_top(b); + return 1; + } + +/* Given a pointer value, compute the next address that is a cache line multiple. */ +#define MOD_EXP_CTIME_ALIGN(x_) \ + ((unsigned char*)(x_) + (MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH - (((BN_ULONG)(x_)) & (MOD_EXP_CTIME_MIN_CACHE_LINE_MASK)))) + +/* This variant of BN_mod_exp_mont() uses fixed windows and the special + * precomputation memory layout to limit data-dependency to a minimum + * to protect secret exponents (cf. the hyper-threading timing attacks + * pointed out by Colin Percival, + * http://www.daemonology.net/hyperthreading-considered-harmful/) + */ +int BN_mod_exp_mont_consttime(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont) + { + int i,bits,ret=0,idx,window,wvalue; + int top; + BIGNUM *r; + const BIGNUM *aa; + BN_MONT_CTX *mont=NULL; + + int numPowers; + unsigned char *powerbufFree=NULL; + int powerbufLen = 0; + unsigned char *powerbuf=NULL; + BIGNUM *computeTemp=NULL, *am=NULL; + + bn_check_top(a); + bn_check_top(p); + bn_check_top(m); + + top = m->top; + + if (!(m->d[0] & 1)) + { + BNerr(BN_F_BN_MOD_EXP_MONT_CONSTTIME,BN_R_CALLED_WITH_EVEN_MODULUS); + return(0); + } + bits=BN_num_bits(p); + if (bits == 0) + { + ret = BN_one(rr); + return ret; + } + + /* Initialize BIGNUM context and allocate intermediate result */ + BN_CTX_start(ctx); + r = BN_CTX_get(ctx); + if (r == NULL) goto err; + + /* Allocate a montgomery context if it was not supplied by the caller. + * If this is not done, things will break in the montgomery part. + */ + if (in_mont != NULL) + mont=in_mont; + else + { + if ((mont=BN_MONT_CTX_new()) == NULL) goto err; + if (!BN_MONT_CTX_set(mont,m,ctx)) goto err; + } + + /* Get the window size to use with size of p. */ + window = BN_window_bits_for_ctime_exponent_size(bits); + + /* Allocate a buffer large enough to hold all of the pre-computed + * powers of a. + */ + numPowers = 1 << window; + powerbufLen = sizeof(m->d[0])*top*numPowers; + if ((powerbufFree=(unsigned char*)OPENSSL_malloc(powerbufLen+MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH)) == NULL) + goto err; + + powerbuf = MOD_EXP_CTIME_ALIGN(powerbufFree); + memset(powerbuf, 0, powerbufLen); + + /* Initialize the intermediate result. Do this early to save double conversion, + * once each for a^0 and intermediate result. + */ + if (!BN_to_montgomery(r,BN_value_one(),mont,ctx)) goto err; + if (!MOD_EXP_CTIME_COPY_TO_PREBUF(r, top, powerbuf, 0, numPowers)) goto err; + + /* Initialize computeTemp as a^1 with montgomery precalcs */ + computeTemp = BN_CTX_get(ctx); + am = BN_CTX_get(ctx); + if (computeTemp==NULL || am==NULL) goto err; + + if (a->neg || BN_ucmp(a,m) >= 0) + { + if (!BN_mod(am,a,m,ctx)) + goto err; + aa= am; + } + else + aa=a; + if (!BN_to_montgomery(am,aa,mont,ctx)) goto err; + if (!BN_copy(computeTemp, am)) goto err; + if (!MOD_EXP_CTIME_COPY_TO_PREBUF(am, top, powerbuf, 1, numPowers)) goto err; + + /* If the window size is greater than 1, then calculate + * val[i=2..2^winsize-1]. Powers are computed as a*a^(i-1) + * (even powers could instead be computed as (a^(i/2))^2 + * to use the slight performance advantage of sqr over mul). + */ + if (window > 1) + { + for (i=2; i= 0) + { + wvalue=0; /* The 'value' of the window */ + + /* Scan the window, squaring the result as we go */ + for (i=0; id[0] & 1)) + if (!BN_is_odd(m)) { BNerr(BN_F_BN_MOD_EXP_MONT_WORD,BN_R_CALLED_WITH_EVEN_MODULUS); return(0); } + if (m->top == 1) + a %= m->d[0]; /* make sure that 'a' is reduced */ + bits = BN_num_bits(p); if (bits == 0) { - BN_one(rr); - return(1); + /* x**0 mod 1 is still zero. */ + if (BN_is_one(m)) + { + ret = 1; + BN_zero(rr); + } + else + ret = BN_one(rr); + return ret; + } + if (a == 0) + { + BN_zero(rr); + ret = 1; + return ret; } + BN_CTX_start(ctx); d = BN_CTX_get(ctx); r = BN_CTX_get(ctx); @@ -586,48 +875,61 @@ int BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p, err: if ((in_mont == NULL) && (mont != NULL)) BN_MONT_CTX_free(mont); BN_CTX_end(ctx); + bn_check_top(rr); return(ret); } /* The old fallback, simple version :-) */ -int BN_mod_exp_simple(BIGNUM *r, - const BIGNUM *a, const BIGNUM *p, const BIGNUM *m, - BN_CTX *ctx) +int BN_mod_exp_simple(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx) { - int i,j,bits,ret=0,wstart,wend,window,wvalue,ts=0; + int i,j,bits,ret=0,wstart,wend,window,wvalue; int start=1; BIGNUM *d; - BIGNUM val[TABLE_SIZE]; + /* Table of variables obtained from 'ctx' */ + BIGNUM *val[TABLE_SIZE]; + + if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) + { + /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */ + BNerr(BN_F_BN_MOD_EXP_SIMPLE,ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return -1; + } bits=BN_num_bits(p); if (bits == 0) { - BN_one(r); - return(1); + ret = BN_one(r); + return ret; } BN_CTX_start(ctx); - if ((d = BN_CTX_get(ctx)) == NULL) goto err; + d = BN_CTX_get(ctx); + val[0] = BN_CTX_get(ctx); + if(!d || !val[0]) goto err; - BN_init(&(val[0])); - ts=1; - if (!BN_mod(&(val[0]),a,m,ctx)) goto err; /* 1 */ + if (!BN_nnmod(val[0],a,m,ctx)) goto err; /* 1 */ + if (BN_is_zero(val[0])) + { + BN_zero(r); + ret = 1; + goto err; + } window = BN_window_bits_for_exponent_size(bits); if (window > 1) { - if (!BN_mod_mul(d,&(val[0]),&(val[0]),m,ctx)) + if (!BN_mod_mul(d,val[0],val[0],m,ctx)) goto err; /* 2 */ j=1<<(window-1); for (i=1; i>1]),m,ctx)) + if (!BN_mod_mul(r,r,val[wvalue>>1],m,ctx)) goto err; /* move the 'window' down further */ @@ -691,8 +993,7 @@ int BN_mod_exp_simple(BIGNUM *r, ret=1; err: BN_CTX_end(ctx); - for (i=0; i