/* $Id$ */ /* * Copyright (C) 1998-2002 RSA Security Inc. All rights reserved. * * This work contains proprietary information of RSA Security. * Distribution is limited to authorized licensees of RSA * Security. Any unauthorized reproduction, distribution or * modification of this work is strictly prohibited. * */ /** * @file bn_div.c * @brief Division and modulus functions */ #include "bn_lcl.h" #if !(defined(NO_SPLIT) && defined(SPLIT_FILE)) #ifdef NO_SPLIT #define SPLIT_BN_DIV #define SPLIT_BN_MOD #endif /* NO_SPLIT */ #ifdef SPLIT_BN_DIV /* The old slow way */ #ifdef OLD_BN_DIVISION int BN_div(dv, rem, m, d,ctx) BIGNUM *dv; BIGNUM *rem; BIGNUM *m; BIGNUM *d; BN_CTX *ctx; { int i,nm,nd; BIGNUM *D; bn_check_top(m); bn_check_top(d); if (BN_is_zero(d)) { #ifndef NO_ERR BNerr(BN_F_BN_DIV,BN_R_DIV_BY_ZERO); #endif return(0); } if (BN_ucmp(m,d) < 0) { if (rem != NULL) { if (BN_copy(rem,m) == NULL) return(0); } if (dv != NULL) BN_zero(dv); return(1); } D= &(ctx->bn[ctx->tos]); if (dv == NULL) dv= &(ctx->bn[ctx->tos+1]); if (rem == NULL) rem= &(ctx->bn[ctx->tos+2]); nd=BN_num_bits(d); nm=BN_num_bits(m); if (BN_copy(D,d) == NULL) return(0); if (BN_copy(rem,m) == NULL) return(0); /* The next 2 are needed so we can do a dv->d[0]|=1 later * since BN_lshift1 will only work once there is a value */ BN_zero(dv); bn_wexpand(dv,1); dv->top=1; if (!BN_lshift(D,D,nm-nd)) return(0); for (i=nm-nd; i>=0; i--) { if (!BN_lshift1(dv,dv)) return(0); if (BN_ucmp(rem,D) >= 0) { dv->d[0]|=1; if (!BN_usub(rem,rem,D)) return(0); } /* CAN IMPROVE (and have now :=) */ if (!BN_rshift1(D,D)) return(0); } rem->neg=BN_is_zero(rem)?0:m->neg; dv->neg=m->neg^d->neg; return(1); } #else /** * Performs Big number division * * @param dv [In] division * @param rm [Out] remainder * @param num [In] number * @param divisor [In] divisor * @param ctx [In] Temporary data storage * * @todo add more comments to this function */ int BN_div(BIGNUM *dv, BIGNUM *rm, BIGNUM *num, BIGNUM *divisor, BN_CTX *ctx) { int norm_shift,i,j,loop; BIGNUM *tmp,wnum,*snum,*sdiv,*res; BN_ULONG *resp,*wnump; BN_ULONG d0,d1; int num_n,div_n; bn_check_top(num); bn_check_top(divisor); if (BN_is_zero(divisor)) { #ifndef NO_ERR BNerr(BN_F_BN_DIV,BN_R_DIV_BY_ZERO); #endif return(0); } if (BN_ucmp(num,divisor) < 0) { if (rm != NULL) { if (BN_copy(rm,num) == NULL) return(0); } if (dv != NULL) (void)BN_zero(dv); return(1); } tmp= &(ctx->bn[ctx->tos]); tmp->neg=0; snum= &(ctx->bn[ctx->tos+1]); sdiv= &(ctx->bn[ctx->tos+2]); if (dv == NULL) res= &(ctx->bn[ctx->tos+3]); else res=dv; /* First we normalise the numbers */ norm_shift=BN_BITS2-((BN_num_bits(divisor))%BN_BITS2); if (!BN_lshift(sdiv,divisor,norm_shift)) return(0); sdiv->neg=0; norm_shift+=BN_BITS2; if (!BN_lshift(snum,num,norm_shift)) return(0); snum->neg=0; 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 */ BN_init(&wnum); wnum.d= &(snum->d[loop]); wnum.top= div_n; wnum.max= snum->max+1; /* a bit of a lie */ /* Get the top 2 words of sdiv */ /* i=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' */ if (!bn_wexpand(res,(loop+1))) goto err; res->neg= (num->neg^divisor->neg); res->top=loop; resp= &(res->d[loop-1]); /* space for temp */ if (!bn_wexpand(tmp,(div_n+1))) goto err; if (BN_ucmp(&wnum,sdiv) >= 0) { if (!BN_usub(&wnum,&wnum,sdiv)) goto err; *resp=1; res->d[res->top-1]=1; } else res->top--; resp--; for (i=0; i>BN_BITS2) || (t2 <= ((BN_ULLONG)(rem< t1l) t3h++; t3h=(t1h-t3h)&BN_MASK2; /*if ((t3>>BN_BITS2) || (t2 <= ((t3<d,sdiv->d,div_n,q); tmp->d[div_n]=l0; for (j=div_n+1; j>0; j--) if (tmp->d[j-1]) break; tmp->top=j; j=wnum.top; #ifdef BN_DEBUG /* Sometimes this is 0 now (wnum.top is not). * We need to look at this some time, I am quite * sure it has no affect. */ bn_fix_top(&wnum); #endif (void)BN_sub(&wnum,&wnum,tmp); snum->top=snum->top+wnum.top-j; if (wnum.neg) { q--; j=wnum.top; (void)BN_add(&wnum,&wnum,sdiv); snum->top+=wnum.top-j; } *(resp--)=q; wnump--; } bn_fix_top(snum); if (rm != NULL) { i=num->neg; /* just in case num == rm */ if (BN_rshift(rm,snum,norm_shift) == 0) return(0); rm->neg=i; } return(1); err: return(0); } #endif #endif #ifdef SPLIT_BN_MOD /** * Calculate the remainder where rem = m mod d * * @param rem [Out] Modulus result * @param m [In] Base value * @param d [In] Divisor * @param ctx [In] BN_CTX for data space * * @pre variables are initialised and valid * @post rem contained modulus * * @retval 1 success * @retval 0 failure * * @note when BN_LIBRARY_SMALL define uses following algorithm * otherwise calls through to BN_div * * @note m < d rem = m * (length m = length of d) and (m > d) , rem = m - d * alg otherwise * let rem = m * loop while rem > d * let dv = shift left d, n bits to = length rem * if dv > rem * let dv = shift left d, n - 1 bits ( = length rem - 1) * end if * let rem = rem - dv; * end loop * * @relates BN_div */ int BN_mod(BIGNUM *rem, BIGNUM *m, BIGNUM *d, BN_CTX *ctx) { #ifdef BN_LIBRARY_SMALL int nm,nd; BIGNUM *dv, *dv2; /* if m < d , mod = m */ if (BN_ucmp(m,d) < 0) return((BN_copy(rem,m) == NULL)?0:1); dv= &(ctx->bn[ctx->tos]); dv2 = &(ctx->bn[ctx->tos+1]); nm=BN_num_bits(m); nd=BN_num_bits(d); /* if bits m = bits d and m >= d (from above) * rem = m - d */ if(nm == nd) { BN_usub(rem, m, d); goto end; } if (BN_copy(rem, m) == NULL) { return 0; } /* while the rem > d */ while(BN_ucmp(rem,d) > 0) { nm=BN_num_bits(rem); if(!BN_lshift(dv2,d,nm-nd)) { return(0); } if(BN_ucmp(dv2, rem) >0) { if(!BN_lshift(dv2,d,nm-nd-1)) { return(0); } } BN_usub(rem,rem,dv2); } end: return(1); #else return(BN_div(NULL,rem,m,d,ctx)); #endif } #endif #endif