Commit bd31fb21 authored by Bodo Möller's avatar Bodo Möller
Browse files

Change to mitigate branch prediction attacks

Submitted by: Matthew D Wood
Reviewed by: Bodo Moeller
parent b506821d
Loading
Loading
Loading
Loading
+38 −1
Original line number Diff line number Diff line
@@ -483,6 +483,43 @@

 Changes between 0.9.8e and 0.9.8f  [xx XXX xxxx]

  *) Mitigate branch prediction attacks, which can be practical if a
     single processor is shared, allowing a spy process to extract
     information.  For detailed background information, see
     http://eprint.iacr.org/2007/039 (O. Aciicmez, S. Gueron,
     J.-P. Seifert, "New Branch Prediction Vulnerabilities in OpenSSL
     and Necessary Software Countermeasures").  The core of the change
     are new versions BN_div_no_branch() and
     BN_mod_inverse_no_branch() of BN_div() and BN_mod_inverse(),
     respectively, which are slower, but avoid the security-relevant
     conditional branches.  These are automatically called by BN_div()
     and BN_mod_inverse() if the flag BN_FLG_CONSTTIME is set for the
     modulus.  Also, BN_is_bit_set() has been changed to remove a
     conditional branch.

     BN_FLG_CONSTTIME is the new name for the previous
     BN_FLG_EXP_CONSTTIME flag, since it now affects more than just
     modular exponentiation.  (Since OpenSSL 0.9.7h, setting this flag
     in the exponent causes BN_mod_exp_mont() to use the alternative
     implementation in BN_mod_exp_mont_consttime().)  The old name
     remains as a deprecated alias.

     Similary, RSA_FLAG_NO_EXP_CONSTTIME is replaced by a more general
     RSA_FLAG_NO_CONSTTIME flag since the RSA implementation now uses
     constant-time implementations for more than just exponentiation.
     Here too the old name is kept as a deprecated alias.

     BN_BLINDING_new() will now use BN_dup() for the modulus so that
     the BN_BLINDING structure gets an independent copy of the
     modulus.  This means that the previous "BIGNUM *m" argument to
     BN_BLINDING_new() and to BN_BLINDING_create_param() now
     essentially becomes "const BIGNUM *m", although we can't actually
     change this in the header file before 0.9.9.  It allows
     RSA_setup_blinding() to use BN_with_flags() on the modulus to
     enable BN_FLG_CONSTTIME.

     [Matthew D Wood (Intel Corp)]

  *) In the SSL/TLS server implementation, be strict about session ID
     context matching (which matters if an application uses a single
     external cache for different purposes).  Previously,
+16 −2
Original line number Diff line number Diff line
@@ -256,8 +256,18 @@ extern "C" {

#define BN_FLG_MALLOCED		0x01
#define BN_FLG_STATIC_DATA	0x02
#define BN_FLG_EXP_CONSTTIME	0x04 /* avoid leaking exponent information through timings
#define BN_FLG_CONSTTIME	0x04 /* avoid leaking exponent information through timing,
                                      * BN_mod_exp_mont() will call BN_mod_exp_mont_consttime,
                                      * BN_div() will call BN_div_no_branch,
                                      * BN_mod_inverse() will call BN_mod_inverse_no_branch.
                                      */

#ifndef OPENSSL_NO_DEPRECATED
#define BN_FLG_EXP_CONSTTIME BN_FLG_CONSTTIME /* deprecated name for the flag */
                                      /* avoid leaking exponent information through timings
                                      * (BN_mod_exp_mont() will call BN_mod_exp_mont_consttime) */
#endif

#ifndef OPENSSL_NO_DEPRECATED
#define BN_FLG_FREE		0x8000	/* used for debuging */
#endif
@@ -436,6 +446,8 @@ void BN_set_negative(BIGNUM *b, int n);

int	BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d,
	BN_CTX *ctx);
int	BN_div_no_branch(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m,
	const BIGNUM *d, BN_CTX *ctx);
#define BN_mod(rem,m,d,ctx) BN_div(NULL,(rem),(m),(d),(ctx))
int	BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx);
int	BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx);
@@ -505,6 +517,8 @@ int BN_gcd(BIGNUM *r,const BIGNUM *a,const BIGNUM *b,BN_CTX *ctx);
int	BN_kronecker(const BIGNUM *a,const BIGNUM *b,BN_CTX *ctx); /* returns -2 for error */
BIGNUM *BN_mod_inverse(BIGNUM *ret,
	const BIGNUM *a, const BIGNUM *n,BN_CTX *ctx);
BIGNUM *BN_mod_inverse_no_branch(BIGNUM *ret,
	const BIGNUM *A, const BIGNUM *n,BN_CTX *ctx);
BIGNUM *BN_mod_sqrt(BIGNUM *ret,
	const BIGNUM *a, const BIGNUM *n,BN_CTX *ctx);

+7 −1
Original line number Diff line number Diff line
@@ -153,7 +153,12 @@ BN_BLINDING *BN_BLINDING_new(const BIGNUM *A, const BIGNUM *Ai, BIGNUM *mod)
		{
		if ((ret->Ai = BN_dup(Ai)) == NULL) goto err;
		}
	ret->mod = mod;

	/* save a copy of mod in the BN_BLINDING structure */
	if ((ret->mod = BN_dup(mod)) == NULL) goto err;
	if (BN_get_flags(mod, BN_FLG_CONSTTIME) != 0)
		BN_set_flags(ret->mod, BN_FLG_CONSTTIME);

	ret->counter = BN_BLINDING_COUNTER;
	return(ret);
err:
@@ -169,6 +174,7 @@ void BN_BLINDING_free(BN_BLINDING *r)
	if (r->A  != NULL) BN_free(r->A );
	if (r->Ai != NULL) BN_free(r->Ai);
	if (r->e  != NULL) BN_free(r->e );
	if (r->mod != NULL) BN_free(r->mod); 
	OPENSSL_free(r);
	}

+229 −0
Original line number Diff line number Diff line
@@ -185,6 +185,11 @@ int BN_div(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, const BIGNUM *divisor,
	BN_ULONG d0,d1;
	int num_n,div_n;

	if (BN_get_flags(num, BN_FLG_CONSTTIME) != 0)
		{
		return BN_div_no_branch(dv, rm, num, divisor, ctx);
		}

	bn_check_top(dv);
	bn_check_top(rm);
	bn_check_top(num);
@@ -330,6 +335,230 @@ X) -> 0x%08X\n",
			rem=(n1-q*d0)&BN_MASK2;
#endif

#if defined(BN_UMULT_LOHI)
			BN_UMULT_LOHI(t2l,t2h,d1,q);
#elif defined(BN_UMULT_HIGH)
			t2l = d1 * q;
			t2h = BN_UMULT_HIGH(d1,q);
#else
			t2l=LBITS(d1); t2h=HBITS(d1);
			ql =LBITS(q);  qh =HBITS(q);
			mul64(t2l,t2h,ql,qh); /* t2=(BN_ULLONG)d1*q; */
#endif

			for (;;)
				{
				if ((t2h < rem) ||
					((t2h == rem) && (t2l <= wnump[-2])))
					break;
				q--;
				rem += d0;
				if (rem < d0) break; /* don't let rem overflow */
				if (t2l < d1) t2h--; t2l -= d1;
				}
#endif /* !BN_LLONG */
			}
#endif /* !BN_DIV3W */

		l0=bn_mul_words(tmp->d,sdiv->d,div_n,q);
		tmp->d[div_n]=l0;
		wnum.d--;
		/* ingore top values of the bignums just sub the two 
		 * BN_ULONG arrays with bn_sub_words */
		if (bn_sub_words(wnum.d, wnum.d, tmp->d, div_n+1))
			{
			/* Note: As we have considered only the leading
			 * two BN_ULONGs in the calculation of q, sdiv * q
			 * might be greater than wnum (but then (q-1) * sdiv
			 * is less or equal than wnum)
			 */
			q--;
			if (bn_add_words(wnum.d, wnum.d, sdiv->d, div_n))
				/* we can't have an overflow here (assuming
				 * that q != 0, but if q == 0 then tmp is
				 * zero anyway) */
				(*wnump)++;
			}
		/* store part of the result */
		*resp = q;
		}
	bn_correct_top(snum);
	if (rm != NULL)
		{
		/* Keep a copy of the neg flag in num because if rm==num
		 * BN_rshift() will overwrite it.
		 */
		int neg = num->neg;
		BN_rshift(rm,snum,norm_shift);
		if (!BN_is_zero(rm))
			rm->neg = neg;
		bn_check_top(rm);
		}
	BN_CTX_end(ctx);
	return(1);
err:
	bn_check_top(rm);
	BN_CTX_end(ctx);
	return(0);
	}


/* BN_div_no_branch is a special version of BN_div. It does not contain
 * branches that may leak sensitive information.
 */
int BN_div_no_branch(BIGNUM *dv, BIGNUM *rm, const BIGNUM *num, 
	const BIGNUM *divisor, BN_CTX *ctx)
	{
	int norm_shift,i,loop;
	BIGNUM *tmp,wnum,*snum,*sdiv,*res;
	BN_ULONG *resp,*wnump;
	BN_ULONG d0,d1;
	int num_n,div_n;

	bn_check_top(dv);
	bn_check_top(rm);
	bn_check_top(num);
	bn_check_top(divisor);

	if (BN_is_zero(divisor))
		{
		BNerr(BN_F_BN_DIV,BN_R_DIV_BY_ZERO);
		return(0);
		}

	BN_CTX_start(ctx);
	tmp=BN_CTX_get(ctx);
	snum=BN_CTX_get(ctx);
	sdiv=BN_CTX_get(ctx);
	if (dv == NULL)
		res=BN_CTX_get(ctx);
	else	res=dv;
	if (sdiv == NULL || res == NULL) goto err;

	/* First we normalise the numbers */
	norm_shift=BN_BITS2-((BN_num_bits(divisor))%BN_BITS2);
	if (!(BN_lshift(sdiv,divisor,norm_shift))) goto err;
	sdiv->neg=0;
	norm_shift+=BN_BITS2;
	if (!(BN_lshift(snum,num,norm_shift))) goto err;
	snum->neg=0;

	/* Since we don't know whether snum is larger than sdiv,
	 * we pad snum with enough zeroes without changing its
	 * value. 
	 */
	if (snum->top <= sdiv->top+1) 
		{
		if (bn_wexpand(snum, sdiv->top + 2) == NULL) goto err;
		for (i = snum->top; i < sdiv->top + 2; i++) snum->d[i] = 0;
		snum->top = sdiv->top + 2;
		}
	else
		{
		if (bn_wexpand(snum, snum->top + 1) == NULL) goto err;
		snum->d[snum->top] = 0;
		snum->top ++;
		}

	div_n=sdiv->top;
	num_n=snum->top;
	loop=num_n-div_n;
	/* Lets setup a 'window' into snum
	 * This is the part that corresponds to the current
	 * 'area' being divided */
	wnum.neg   = 0;
	wnum.d     = &(snum->d[loop]);
	wnum.top   = div_n;
	/* only needed when BN_ucmp messes up the values between top and max */
	wnum.dmax  = snum->dmax - loop; /* so we don't step out of bounds */

	/* Get the top 2 words of sdiv */
	/* div_n=sdiv->top; */
	d0=sdiv->d[div_n-1];
	d1=(div_n == 1)?0:sdiv->d[div_n-2];

	/* pointer to the 'top' of snum */
	wnump= &(snum->d[num_n-1]);

	/* Setup to 'res' */
	res->neg= (num->neg^divisor->neg);
	if (!bn_wexpand(res,(loop+1))) goto err;
	res->top=loop-1;
	resp= &(res->d[loop-1]);

	/* space for temp */
	if (!bn_wexpand(tmp,(div_n+1))) goto err;

	/* if res->top == 0 then clear the neg value otherwise decrease
	 * the resp pointer */
	if (res->top == 0)
		res->neg = 0;
	else
		resp--;

	for (i=0; i<loop-1; i++, wnump--, resp--)
		{
		BN_ULONG q,l0;
		/* the first part of the loop uses the top two words of
		 * snum and sdiv to calculate a BN_ULONG q such that
		 * | wnum - sdiv * q | < sdiv */
#if defined(BN_DIV3W) && !defined(OPENSSL_NO_ASM)
		BN_ULONG bn_div_3_words(BN_ULONG*,BN_ULONG,BN_ULONG);
		q=bn_div_3_words(wnump,d1,d0);
#else
		BN_ULONG n0,n1,rem=0;

		n0=wnump[0];
		n1=wnump[-1];
		if (n0 == d0)
			q=BN_MASK2;
		else 			/* n0 < d0 */
			{
#ifdef BN_LLONG
			BN_ULLONG t2;

#if defined(BN_LLONG) && defined(BN_DIV2W) && !defined(bn_div_words)
			q=(BN_ULONG)(((((BN_ULLONG)n0)<<BN_BITS2)|n1)/d0);
#else
			q=bn_div_words(n0,n1,d0);
#ifdef BN_DEBUG_LEVITTE
			fprintf(stderr,"DEBUG: bn_div_words(0x%08X,0x%08X,0x%08\
X) -> 0x%08X\n",
				n0, n1, d0, q);
#endif
#endif

#ifndef REMAINDER_IS_ALREADY_CALCULATED
			/*
			 * rem doesn't have to be BN_ULLONG. The least we
			 * know it's less that d0, isn't it?
			 */
			rem=(n1-q*d0)&BN_MASK2;
#endif
			t2=(BN_ULLONG)d1*q;

			for (;;)
				{
				if (t2 <= ((((BN_ULLONG)rem)<<BN_BITS2)|wnump[-2]))
					break;
				q--;
				rem += d0;
				if (rem < d0) break; /* don't let rem overflow */
				t2 -= d1;
				}
#else /* !BN_LLONG */
			BN_ULONG t2l,t2h,ql,qh;

			q=bn_div_words(n0,n1,d0);
#ifdef BN_DEBUG_LEVITTE
			fprintf(stderr,"DEBUG: bn_div_words(0x%08X,0x%08X,0x%08\
X) -> 0x%08X\n",
				n0, n1, d0, q);
#endif
#ifndef REMAINDER_IS_ALREADY_CALCULATED
			rem=(n1-q*d0)&BN_MASK2;
#endif

#if defined(BN_UMULT_LOHI)
			BN_UMULT_LOHI(t2l,t2h,d1,q);
#elif defined(BN_UMULT_HIGH)
+10 −10
Original line number Diff line number Diff line
@@ -122,9 +122,9 @@ 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_EXP_CONSTTIME) != 0)
	if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0)
		{
		/* BN_FLG_EXP_CONSTTIME only supported by BN_mod_exp_mont() */
		/* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
		BNerr(BN_F_BN_EXP,ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
		return -1;
		}
@@ -213,7 +213,7 @@ int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
	if (BN_is_odd(m))
		{
#  ifdef MONT_EXP_WORD
		if (a->top == 1 && !a->neg && (BN_get_flags(p, BN_FLG_EXP_CONSTTIME) == 0))
		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);
@@ -245,9 +245,9 @@ int BN_mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
	BIGNUM *val[TABLE_SIZE];
	BN_RECP_CTX recp;

	if (BN_get_flags(p, BN_FLG_EXP_CONSTTIME) != 0)
	if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0)
		{
		/* BN_FLG_EXP_CONSTTIME only supported by BN_mod_exp_mont() */
		/* 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;
		}
@@ -379,7 +379,7 @@ int BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
	BIGNUM *val[TABLE_SIZE];
	BN_MONT_CTX *mont=NULL;

	if (BN_get_flags(p, BN_FLG_EXP_CONSTTIME) != 0)
	if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0)
		{
		return BN_mod_exp_mont_consttime(rr, a, p, m, ctx, in_mont);
		}
@@ -745,9 +745,9 @@ int BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p,
#define BN_TO_MONTGOMERY_WORD(r, w, mont) \
		(BN_set_word(r, (w)) && BN_to_montgomery(r, r, (mont), ctx))

	if (BN_get_flags(p, BN_FLG_EXP_CONSTTIME) != 0)
	if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0)
		{
		/* BN_FLG_EXP_CONSTTIME only supported by BN_mod_exp_mont() */
		/* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
		BNerr(BN_F_BN_MOD_EXP_MONT_WORD,ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
		return -1;
		}
@@ -881,9 +881,9 @@ int BN_mod_exp_simple(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
	/* Table of variables obtained from 'ctx' */
	BIGNUM *val[TABLE_SIZE];

	if (BN_get_flags(p, BN_FLG_EXP_CONSTTIME) != 0)
	if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0)
		{
		/* BN_FLG_EXP_CONSTTIME only supported by BN_mod_exp_mont() */
		/* 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;
		}
Loading