From 33c5019985d0464f5a8e21bfd7dde3389cf9534c Mon Sep 17 00:00:00 2001 From: Tom St Denis Date: Fri, 28 Feb 2003 16:07:58 +0000 Subject: [PATCH] added libtommath-0.11 --- b.bat | 5 +- bn.c | 152 ++++--- bn.h | 3 + bn.pdf | Bin 170220 -> 172711 bytes bn.tex | 1255 ++++++++++++++++++++++++++------------------------- changes.txt | 146 +++--- demo.c | 72 ++- makefile | 12 +- timer.asm | 68 +-- 9 files changed, 885 insertions(+), 828 deletions(-) diff --git a/b.bat b/b.bat index 32dee86..606db8a 100644 --- a/b.bat +++ b/b.bat @@ -1,3 +1,2 @@ -nasm -f coff timer.asm -gcc -Wall -W -O3 -fomit-frame-pointer -funroll-loops -DTIMER_X86 demo.c bn.c timer.o -o ltmdemo -rem gcc -I./mtest/ -DU_MPI -Wall -W -O3 -fomit-frame-pointer -funroll-loops -DTIMER_X86 demo.c mtest/mpi.c timer.o -o mpidemo +nasm -f elf timer.asm +gcc -Wall -W -O3 -fomit-frame-pointer -funroll-loops -DTIMER_X86 demo.c bn.c timer.o -o ltmdemo \ No newline at end of file diff --git a/bn.c b/bn.c index 040ff1c..8175c33 100644 --- a/bn.c +++ b/bn.c @@ -99,7 +99,8 @@ void dump_timings(void) memset(&functime, 0, sizeof(functime)); total = 0; for (x = 0; x < _itims; x++) { - total += timings[x].tot; + if (strcmp(timings[x].func, "_verify")) + total += timings[x].tot; /* try to find this entry */ for (y = 0; functime[y].func != NULL; y++) { @@ -1053,7 +1054,7 @@ static int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) c->dp[digs-1] = (mp_digit)(W[digs-1] & ((mp_word)MP_MASK)); /* clear unused */ - for (ix = c->used; ix < olduse; ix++) { + for (; ix < olduse; ix++) { c->dp[ix] = 0; } @@ -1194,13 +1195,13 @@ static int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) c->used = newused; /* now convert the array W downto what we need */ - for (ix = digs+1; ix < (pa+pb+1); ix++) { + for (ix = digs+1; ix < newused; ix++) { W[ix] += (W[ix-1] >> ((mp_word)DIGIT_BIT)); c->dp[ix-1] = (mp_digit)(W[ix-1] & ((mp_word)MP_MASK)); } c->dp[(pa+pb+1)-1] = (mp_digit)(W[(pa+pb+1)-1] & ((mp_word)MP_MASK)); - for (ix = c->used; ix < oldused; ix++) { + for (; ix < oldused; ix++) { c->dp[ix] = 0; } mp_clamp(c); @@ -1339,17 +1340,17 @@ static int fast_s_mp_sqr(mp_int *a, mp_int *b) b->used = newused; /* now compute digits */ - for (ix = 1; ix < (pa+pa+1); ix++) { + for (ix = 1; ix < newused; ix++) { /* double/add next digit */ W[ix] += W[ix] + W2[ix]; W[ix] = W[ix] + (W[ix-1] >> ((mp_word)DIGIT_BIT)); b->dp[ix-1] = (mp_digit)(W[ix-1] & ((mp_word)MP_MASK)); } - b->dp[(pa+pa+1)-1] = (mp_digit)(W[(pa+pa+1)-1] & ((mp_word)MP_MASK)); + b->dp[(newused)-1] = (mp_digit)(W[(newused)-1] & ((mp_word)MP_MASK)); /* clear high */ - for (ix = b->used; ix < olduse; ix++) { + for (; ix < olduse; ix++) { b->dp[ix] = 0; } @@ -1580,9 +1581,7 @@ static int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c) } mp_clamp(&x0); - mp_clamp(&x1); mp_clamp(&y0); - mp_clamp(&y1); /* now calc the products x0y0 and x1y1 */ if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY) goto X1Y1; /* x0y0 = x0*y0 */ @@ -1679,15 +1678,14 @@ static int mp_karatsuba_sqr(mp_int *a, mp_int *b) x1.used = a->used - B; mp_clamp(&x0); - mp_clamp(&x1); /* now calc the products x0*x0 and x1*x1 */ - if (mp_sqr(&x0, &x0x0) != MP_OKAY) goto X1X1; /* x0x0 = x0*x0 */ - if (mp_sqr(&x1, &x1x1) != MP_OKAY) goto X1X1; /* x1x1 = x1*x1 */ + if (mp_sqr(&x0, &x0x0) != MP_OKAY) goto X1X1; /* x0x0 = x0*x0 */ + if (mp_sqr(&x1, &x1x1) != MP_OKAY) goto X1X1; /* x1x1 = x1*x1 */ /* now calc x1-x0 and y1-y0 */ if (mp_sub(&x1, &x0, &t1) != MP_OKAY) goto X1X1; /* t1 = x1 - x0 */ - if (mp_sqr(&t1, &t1) != MP_OKAY) goto X1X1; /* t1 = (x1 - x0) * (y1 - y0) */ + if (mp_sqr(&t1, &t1) != MP_OKAY) goto X1X1; /* t1 = (x1 - x0) * (y1 - y0) */ /* add x0y0 */ if (mp_add(&x0x0, &x1x1, &t2) != MP_OKAY) goto X1X1; /* t2 = x0y0 + x1y1 */ @@ -2760,8 +2758,7 @@ int mp_reduce_setup(mp_int *a, mp_int *b) VERIFY(a); VERIFY(b); - mp_set(a, 1); - if ((res = mp_lshd(a, b->used * 2)) != MP_OKAY) { + if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { DECFUNC(); return res; } @@ -2876,7 +2873,6 @@ __T: mp_clear(&t); return res; } - /* computes xR^-1 == x (mod N) via Montgomery Reduction (comba) */ static int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp) { @@ -2884,29 +2880,53 @@ static int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp) mp_digit ui; mp_word W[512]; + REGFUNC("fast_mp_montgomery_reduce"); + VERIFY(a); + VERIFY(m); + /* get old used count */ olduse = a->used; /* grow a as required */ - if (a->alloc < m->used*2+1) { - if ((res = mp_grow(a, m->used*2+1)) != MP_OKAY) { + if (a->alloc < m->used+1) { + if ((res = mp_grow(a, m->used+1)) != MP_OKAY) { + DECFUNC(); return res; } } - /* copy and clear */ + /* copy the digits of a */ for (ix = 0; ix < a->used; ix++) { W[ix] = a->dp[ix]; } + + /* zero the high words */ for (; ix < m->used * 2 + 1; ix++) { W[ix] = 0; } - + for (ix = 0; ix < m->used; ix++) { - /* ui = ai * m' mod b */ + /* ui = ai * m' mod b + * + * We avoid a double precision multiplication (which isn't required) + * by casting the value down to a mp_digit. Note this requires that W[ix-1] have + * the carry cleared (see after the inner loop) + */ ui = (((mp_digit)(W[ix] & MP_MASK)) * mp) & MP_MASK; - /* a = a + ui * m * b^i */ + /* a = a + ui * m * b^i + * + * This is computed in place and on the fly. The multiplication + * by b^i is handled by offseting which columns the results + * are added to. + * + * Note the comba method normally doesn't handle carries in the inner loop + * In this case we fix the carry from the previous column since the Montgomery + * reduction requires digits of the result (so far) [see above] to work. This is + * handled by fixing up one carry after the inner loop. The carry fixups are done + * in order so after these loops the first m->used words of W[] have the carries + * fixed + */ { register int iy; register mp_digit *tmpx; @@ -2916,32 +2936,36 @@ static int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp) tmpx = m->dp; _W = W + ix; + /* inner loop */ for (iy = 0; iy < m->used; iy++) { *_W++ += ((mp_word)ui) * ((mp_word)*tmpx++); } - - /* now fix carry for W[ix+1] */ - W[ix+1] += W[ix] >> ((mp_word)DIGIT_BIT); - W[ix] &= ((mp_word)MP_MASK); } + + /* now fix carry for next digit, W[ix+1] */ + W[ix+1] += W[ix] >> ((mp_word)DIGIT_BIT); } /* nox fix rest of carries */ - for (; ix <= m->used * 2 + 1; ix++) { + for (++ix; ix <= m->used * 2 + 1; ix++) { W[ix] += (W[ix-1] >> ((mp_word)DIGIT_BIT)); - W[ix-1] &= ((mp_word)MP_MASK); } - /* copy out */ - - /* A = A/b^n */ + /* copy out, A = A/b^n + * + * The result is A/b^n but instead of converting from an array of mp_word + * to mp_digit than calling mp_rshd we just copy them in the right + * order + */ for (ix = 0; ix < m->used + 1; ix++) { - a->dp[ix] = W[ix+m->used]; + a->dp[ix] = W[ix+m->used] & ((mp_word)MP_MASK); } + /* set the max used */ a->used = m->used + 1; - /* zero oldused digits */ + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits */ for (; ix < olduse; ix++) { a->dp[ix] = 0; } @@ -2951,10 +2975,12 @@ static int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp) /* if A >= m then A = A - m */ if (mp_cmp_mag(a, m) != MP_LT) { if ((res = s_mp_sub(a, m, a)) != MP_OKAY) { + DECFUNC(); return res; } - } + } + DECFUNC(); return MP_OKAY; } @@ -3036,7 +3062,7 @@ int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp) */ static int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) { - mp_int M[64], res; + mp_int M[256], res; mp_digit buf, mp; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; @@ -3048,12 +3074,14 @@ static int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) /* find window size */ x = mp_count_bits(X); - if (x <= 18) { winsize = 2; } - else if (x <= 84) { winsize = 3; } - else if (x <= 300) { winsize = 4; } - else if (x <= 930) { winsize = 5; } - else { winsize = 6; } - + if (x <= 7) { winsize = 2; } + else if (x <= 36) { winsize = 3; } + else if (x <= 140) { winsize = 4; } + else if (x <= 450) { winsize = 5; } + else if (x <= 1303) { winsize = 6; } + else if (x <= 3529) { winsize = 7; } + else { winsize = 8; } + /* init G array */ for (x = 0; x < (1<used)) != MP_OKAY) { + if ((err = mp_2expt(&res, P->used * DIGIT_BIT)) != MP_OKAY) { goto __RES; } - + /* res = R mod m */ if ((err = mp_mod(&res, P, &res)) != MP_OKAY) { goto __RES; @@ -3092,7 +3119,6 @@ static int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) * * The first half of the table is not computed though accept for M[0] and M[1] */ - mp_set(&M[0], 1); if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { goto __RES; } @@ -3101,7 +3127,7 @@ static int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) if ((err = mp_mulmod(&M[1], &res, P, &M[1])) != MP_OKAY) { goto __RES; } - + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ if ((err = mp_copy(&M[1], &M[1<<(winsize-1)])) != MP_OKAY) { goto __RES; @@ -3236,10 +3262,9 @@ __M : return err; } - int mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) { - mp_int M[64], res, mu; + mp_int M[256], res, mu; mp_digit buf; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; @@ -3258,11 +3283,13 @@ int mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) /* find window size */ x = mp_count_bits(X); - if (x <= 18) { winsize = 2; } - else if (x <= 84) { winsize = 3; } - else if (x <= 300) { winsize = 4; } - else if (x <= 930) { winsize = 5; } - else { winsize = 6; } + if (x <= 7) { winsize = 2; } + else if (x <= 36) { winsize = 3; } + else if (x <= 140) { winsize = 4; } + else if (x <= 450) { winsize = 5; } + else if (x <= 1303) { winsize = 6; } + else if (x <= 3529) { winsize = 7; } + else { winsize = 8; } /* init G array */ for (x = 0; x < (1<used = b/DIGIT_BIT + 1; + a->dp[b/DIGIT_BIT] = 1 << (b % DIGIT_BIT); + + return MP_OKAY; +} + + /* find the n'th root of an integer * * Result found such that (c)^b <= a and (c+1)^b > a diff --git a/bn.h b/bn.h index 6e7bc85..5f39cbd 100644 --- a/bn.h +++ b/bn.h @@ -158,6 +158,9 @@ int mp_mul_2(mp_int *a, mp_int *b); /* c = a mod 2^d */ int mp_mod_2d(mp_int *a, int b, mp_int *c); +/* computes a = 2^b */ +int mp_2expt(mp_int *a, int b); + /* ---> Basic arithmetic <--- */ /* b = -a */ diff --git a/bn.pdf b/bn.pdf index f9c86f2398e1dcf66cdb974c737774351bf007bb..b8152e1c1a5b92cb67feddb0c598a6b3794a73f7 100644 GIT binary patch delta 37946 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 delta 35519 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

gz8(4~?+D5HUABK? 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 MdpzE5FYm99-zN&&H~;_u diff --git a/bn.tex b/bn.tex index d2aab27..5c8b73e 100644 --- a/bn.tex +++ b/bn.tex @@ -1,620 +1,635 @@ -\documentclass{article} -\begin{document} - -\title{LibTomMath v0.10 \\ A Free Multiple Precision Integer Library} -\author{Tom St Denis \\ tomstdenis@iahu.ca} -\maketitle -\newpage - -\section{Introduction} -``LibTomMath'' is a free and open source library that provides multiple-precision integer functions required to form a basis -of a public key cryptosystem. LibTomMath is written entire in portable ISO C source code and designed to have an application -interface much like that of MPI from Michael Fromberger. - -LibTomMath was written from scratch by Tom St Denis but designed to be drop in replacement for the MPI package. The -algorithms within the library are derived from descriptions as provided in the Handbook of Applied Cryptography and Knuth's -``The Art of Computer Programming''. The library has been extensively optimized and should provide quite comparable -timings as compared to many free and commercial libraries. - -LibTomMath was designed with the following goals in mind: -\begin{enumerate} -\item Be a drop in replacement for MPI. -\item Be much faster than MPI. -\item Be written entirely in portable C. -\end{enumerate} - -All three goals have been achieved. Particularly the speed increase goal. For example, a 512-bit modular exponentiation -is eight times faster\footnote{On an Athlon XP with GCC 3.2} with LibTomMath compared to MPI. - -Being compatible with MPI means that applications that already use it can be ported fairly quickly. Currently there are -a few differences but there are many similarities. In fact the average MPI based application can be ported in under 15 -minutes. - -Thanks goes to Michael Fromberger for answering a couple questions and Colin Percival for having the patience and courtesy to -help debug and suggest optimizations. They were both of great help! - -\section{Building Against LibTomMath} - -Building against LibTomMath is very simple because there is only one source file. Simply add ``bn.c'' to your project and -copy both ``bn.c'' and ``bn.h'' into your project directory. There is no configuration nor building required before hand. - -If you are porting an MPI application to LibTomMath the first step will be to remove all references to MPI and replace them -with references to LibTomMath. For example, substitute - -\begin{verbatim} -#include "mpi.h" -\end{verbatim} - -with - -\begin{verbatim} -#include "bn.h" -\end{verbatim} - -Remove ``mpi.c'' from your project and replace it with ``bn.c''. - -\section{Programming with LibTomMath} - -\subsection{The mp\_int Structure} -All multiple precision integers are stored in a structure called \textbf{mp\_int}. A multiple precision integer is -essentially an array of \textbf{mp\_digit}. mp\_digit is defined at the top of bn.h. Its type can be changed to suit -a particular platform. - -For example, when \textbf{MP\_8BIT} is defined\footnote{When building bn.c.} a mp\_digit is a unsigned char and holds -seven bits. Similarly when \textbf{MP\_16BIT} is defined a mp\_digit is a unsigned short and holds 15 bits. -By default a mp\_digit is a unsigned long and holds 28 bits. - -The choice of digit is particular to the platform at hand and what available multipliers are provided. For -MP\_8BIT either a $8 \times 8 \Rightarrow 16$ or $16 \times 16 \Rightarrow 16$ multiplier is optimal. When -MP\_16BIT is defined either a $16 \times 16 \Rightarrow 32$ or $32 \times 32 \Rightarrow 32$ multiplier is optimal. By -default a $32 \times 32 \Rightarrow 64$ or $64 \times 64 \Rightarrow 64$ multiplier is optimal. - -This gives the library some flexibility. For example, a i8051 has a $8 \times 8 \Rightarrow 16$ multiplier. The -16-bit x86 instruction set has a $16 \times 16 \Rightarrow 32$ multiplier. In practice this library is not particularly -designed for small devices like an i8051 due to the size. It is possible to strip out functions which are not required -to drop the code size. More realistically the library is well suited to 32 and 64-bit processors that have decent -integer multipliers. The AMD Athlon XP and Intel Pentium 4 processors are examples of well suited processors. - -Throughout the discussions there will be references to a \textbf{used} and \textbf{alloc} members of an integer. The -used member refers to how many digits are actually used in the representation of the integer. The alloc member refers -to how many digits have been allocated off the heap. There is also the $\beta$ quantity which is equal to $2^W$ where -$W$ is the number of bits in a digit (default is 28). - -\subsection{Calling Functions} -Most functions expect pointers to mp\_int's as parameters. To save on memory usage it is possible to have source -variables as destinations. For example: -\begin{verbatim} - mp_add(&x, &y, &x); /* x = x + y */ - mp_mul(&x, &z, &x); /* x = x * z */ - mp_div_2(&x, &x); /* x = x / 2 */ -\end{verbatim} - -\section{Quick Overview} - -\subsection{Basic Functionality} -Essentially all LibTomMath functions return one of three values to indicate if the function worked as desired. A -function will return \textbf{MP\_OKAY} if the function was successful. A function will return \textbf{MP\_MEM} if -it ran out of memory and \textbf{MP\_VAL} if the input was invalid. - -Before an mp\_int can be used it must be initialized with - -\begin{verbatim} -int mp_init(mp_int *a); -\end{verbatim} - -For example, consider the following. - -\begin{verbatim} -#include "bn.h" -int main(void) -{ - mp_int num; - if (mp_init(&num) != MP_OKAY) { - printf("Error initializing a mp_int.\n"); - } - return 0; -} -\end{verbatim} - -A mp\_int can be freed from memory with - -\begin{verbatim} -void mp_clear(mp_int *a); -\end{verbatim} - -This will zero the memory and free the allocated data. There are a set of trivial functions to manipulate the -value of an mp\_int. - -\begin{verbatim} -/* set to zero */ -void mp_zero(mp_int *a); - -/* set to a digit */ -void mp_set(mp_int *a, mp_digit b); - -/* set a 32-bit const */ -int mp_set_int(mp_int *a, unsigned long b); - -/* init to a given number of digits */ -int mp_init_size(mp_int *a, int size); - -/* copy, b = a */ -int mp_copy(mp_int *a, mp_int *b); - -/* inits and copies, a = b */ -int mp_init_copy(mp_int *a, mp_int *b); -\end{verbatim} - -The \textbf{mp\_zero} function will clear the contents of a mp\_int and set it to positive. The \textbf{mp\_set} function -will zero the integer and set the first digit to a value specified. The \textbf{mp\_set\_int} function will zero the -integer and set the first 32-bits to a given value. It is important to note that using mp\_set can have unintended -side effects when either the MP\_8BIT or MP\_16BIT defines are enabled. By default the library will accept the -ranges of values MPI will (and more). - -The \textbf{mp\_init\_size} function will initialize the integer and set the allocated size to a given value. The -allocated digits are zero'ed by default but not marked as used. The \textbf{mp\_copy} function will copy the digits -(and sign) of the first parameter into the integer specified by the second parameter. The \textbf{mp\_init\_copy} will -initialize the first integer specified and copy the second one into it. Note that the order is reversed from that of -mp\_copy. This odd ``bug'' was kept to maintain compatibility with MPI. - -\subsection{Digit Manipulations} - -There are a class of functions that provide simple digit manipulations such as shifting and modulo reduction of powers -of two. - -\begin{verbatim} -/* right shift by "b" digits */ -void mp_rshd(mp_int *a, int b); - -/* left shift by "b" digits */ -int mp_lshd(mp_int *a, int b); - -/* c = a / 2^b */ -int mp_div_2d(mp_int *a, int b, mp_int *c); - -/* b = a/2 */ -int mp_div_2(mp_int *a, mp_int *b); - -/* c = a * 2^b */ -int mp_mul_2d(mp_int *a, int b, mp_int *c); - -/* b = a*2 */ -int mp_mul_2(mp_int *a, mp_int *b); - -/* c = a mod 2^d */ -int mp_mod_2d(mp_int *a, int b, mp_int *c); -\end{verbatim} - -\subsection{Basic Arithmetic} - -Next are the class of functions which provide basic arithmetic. - -\begin{verbatim} -/* b = -a */ -int mp_neg(mp_int *a, mp_int *b); - -/* b = |a| */ -int mp_abs(mp_int *a, mp_int *b); - -/* compare a to b */ -int mp_cmp(mp_int *a, mp_int *b); - -/* compare |a| to |b| */ -int mp_cmp_mag(mp_int *a, mp_int *b); - -/* c = a + b */ -int mp_add(mp_int *a, mp_int *b, mp_int *c); - -/* c = a - b */ -int mp_sub(mp_int *a, mp_int *b, mp_int *c); - -/* c = a * b */ -int mp_mul(mp_int *a, mp_int *b, mp_int *c); - -/* b = a^2 */ -int mp_sqr(mp_int *a, mp_int *b); - -/* a/b => cb + d == a */ -int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); - -/* c = a mod b, 0 <= c < b */ -int mp_mod(mp_int *a, mp_int *b, mp_int *c); -\end{verbatim} - -\subsection{Single Digit Functions} - -\begin{verbatim} -/* compare against a single digit */ -int mp_cmp_d(mp_int *a, mp_digit b); - -/* c = a + b */ -int mp_add_d(mp_int *a, mp_digit b, mp_int *c); - -/* c = a - b */ -int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); - -/* c = a * b */ -int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); - -/* a/b => cb + d == a */ -int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); - -/* c = a mod b, 0 <= c < b */ -int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); -\end{verbatim} - -Note that care should be taken for the value of the digit passed. By default, any 28-bit integer is a valid digit that can -be passed into the function. However, if MP\_8BIT or MP\_16BIT is defined only 7 or 15-bit (respectively) integers -can be passed into it. - -\subsection{Modular Arithmetic} - -There are some trivial modular arithmetic functions. - -\begin{verbatim} -/* d = a + b (mod c) */ -int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); - -/* d = a - b (mod c) */ -int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); - -/* d = a * b (mod c) */ -int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); - -/* c = a * a (mod b) */ -int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); - -/* c = 1/a (mod b) */ -int mp_invmod(mp_int *a, mp_int *b, mp_int *c); - -/* c = (a, b) */ -int mp_gcd(mp_int *a, mp_int *b, mp_int *c); - -/* c = [a, b] or (a*b)/(a, b) */ -int mp_lcm(mp_int *a, mp_int *b, mp_int *c); - -/* find the b'th root of a */ -int mp_n_root(mp_int *a, mp_digit b, mp_int *c); - -/* computes the jacobi c = (a | n) (or Legendre if b is prime) */ -int mp_jacobi(mp_int *a, mp_int *n, int *c); - -/* used to setup the Barrett reduction for a given modulus b */ -int mp_reduce_setup(mp_int *a, mp_int *b); - -/* Barrett Reduction, computes a (mod b) with a precomputed value c - * - * Assumes that 0 < a <= b^2, note if 0 > a > -(b^2) then you can merely - * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. - */ -int mp_reduce(mp_int *a, mp_int *b, mp_int *c); - -/* setups the montgomery reduction */ -int mp_montgomery_setup(mp_int *a, mp_digit *mp); - -/* computes xR^-1 == x (mod N) via Montgomery Reduction */ -int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); - -/* d = a^b (mod c) */ -int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); -\end{verbatim} - -\subsection{Radix Conversions} -To read or store integers in other formats there are the following functions. - -\begin{verbatim} -int mp_unsigned_bin_size(mp_int *a); -int mp_read_unsigned_bin(mp_int *a, unsigned char *b, int c); -int mp_to_unsigned_bin(mp_int *a, unsigned char *b); - -int mp_signed_bin_size(mp_int *a); -int mp_read_signed_bin(mp_int *a, unsigned char *b, int c); -int mp_to_signed_bin(mp_int *a, unsigned char *b); - -int mp_read_radix(mp_int *a, unsigned char *str, int radix); -int mp_toradix(mp_int *a, unsigned char *str, int radix); -int mp_radix_size(mp_int *a, int radix); -\end{verbatim} - -The integers are stored in big endian format as most libraries (and MPI) expect. The \textbf{mp\_read\_radix} and -\textbf{mp\_toradix} functions read and write (respectively) null terminated ASCII strings in a given radix. Valid values -for the radix are between 2 and 64 (inclusively). - -\section{Function Analysis} - -Throughout the function analysis the variable $N$ will denote the average size of an input to a function as measured -by the number of digits it has. The variable $W$ will denote the number of bits per word and $c$ will denote a small -constant amount of work. The big-oh notation will be abused slightly to consider numbers that do not grow to infinity. -That is we shall consider $O(N/2) \ne O(N)$ which is an abuse of the notation. - -\subsection{Digit Manipulation Functions} -The class of digit manipulation functions such as \textbf{mp\_rshd}, \textbf{mp\_lshd} and \textbf{mp\_mul\_2} are all -very simple functions to analyze. - -\subsubsection{mp\_rshd(mp\_int *a, int b)} -Shifts $a$ by given number of digits to the right and is equivalent to dividing by $\beta^b$. The work is performed -in-place which means the input and output are the same. If the shift count $b$ is less than or equal to zero -the function returns without doing any work. If the the shift count is larger than the number of digits in $a$ -then $a$ is simply zeroed without shifting digits. - -This function requires no additional memory and $O(N)$ time. - -\subsubsection{mp\_lshd(mp\_int *a, int b)} -Shifts $a$ by a given number of digits to the left and is equivalent to multiplying by $\beta^b$. The work -is performed in-place which means the input and output are the same. If the shift count $b$ is less than or equal -to zero the function returns success without doing any work. - -This function requires $O(b)$ additional digits of memory and $O(N)$ time. - -\subsubsection{mp\_div\_2d(mp\_int *a, int b, mp\_int *c, mp\_int *d)} -Shifts $a$ by a given number of \textbf{bits} to the right and is equivalent to dividing by $2^b$. The shifted number is stored -in the $c$ parameter. The remainder of $a/2^b$ is optionally stored in $d$ (if it is not passed as NULL). -If the shift count $b$ is less than or equal to zero the function places $a$ in $c$ and returns success. - -This function requires $O(2 \cdot N)$ additional digits of memory and $O(2 \cdot N)$ time. - -\subsubsection{mp\_mul\_2d(mp\_int *a, int b, mp\_int *c)} -Shifts $a$ by a given number of bits to the left and is equivalent to multiplying by $2^b$. The shifted number -is placed in the $c$ parameter. If the shift count $b$ is less than or equal to zero the function places $a$ -in $c$ and returns success. - -This function requires $O(N)$ additional digits of memory and $O(2 \cdot N)$ time. - -\subsubsection{mp\_mod\_2d(mp\_int *a, int b, mp\_int *c)} -Performs the action of reducing $a$ modulo $2^b$ and stores the result in $c$. If the shift count $b$ is less than -or equal to zero the function places $a$ in $c$ and returns success. - -This function requires $O(N)$ additional digits of memory and $O(2 \cdot N)$ time. - -\subsection{Basic Arithmetic} - -\subsubsection{mp\_cmp(mp\_int *a, mp\_int *b)} -Performs a \textbf{signed} comparison between $a$ and $b$ returning \textbf{MP\_GT} is $a$ is larger than $b$. - -This function requires no additional memory and $O(N)$ time. - -\subsubsection{mp\_cmp\_mag(mp\_int *a, mp\_int *b)} -Performs a \textbf{unsigned} comparison between $a$ and $b$ returning \textbf{MP\_GT} is $a$ is larger than $b$. Note -that this comparison is unsigned which means it will report, for example, $-5 > 3$. By comparison mp\_cmp will -report $-5 < 3$. - -This function requires no additional memory and $O(N)$ time. - -\subsubsection{mp\_add(mp\_int *a, mp\_int *b, mp\_int *c)} -Computes $c = a + b$ using signed arithmetic. Handles the sign of the numbers which means it will subtract as -required, e.g. $a + -b$ turns into $a - b$. - -This function requires no additional memory and $O(N)$ time. - -\subsubsection{mp\_sub(mp\_int *a, mp\_int *b, mp\_int *c)} -Computes $c = a - b$ using signed arithmetic. Handles the sign of the numbers which means it will add as -required, e.g. $a - -b$ turns into $a + b$. - -This function requires no additional memory and $O(N)$ time. - -\subsubsection{mp\_mul(mp\_int *a, mp\_int *b, mp\_int *c)} -Computes $c = a \cdot b$ using signed arithmetic. Handles the sign of the numbers correctly which means it will -correct the sign of the product as required, e.g. $a \cdot -b$ turns into $-ab$. - -For relatively small inputs, that is less than 80 digits a standard baseline or comba-baseline multiplier is used. It -requires no additional memory and $O(N^2)$ time. The comba-baseline multiplier is only used if it can safely be used -without losing carry digits. The comba method is faster than the baseline method but cannot always be used which is why -both are provided. The code will automatically determine when it can be used. If the digit count is higher -than 80 for the inputs than a Karatsuba multiplier is used which requires approximately $O(6 \cdot N)$ memory and -$O(N^{lg(3)})$ time. - -\subsubsection{mp\_sqr(mp\_int *a, mp\_int *b)} -Computes $b = a^2$. -For relatively small inputs, that is less than 80 digits a modified squaring or comba-squaring algorithm is used. It -requires no additional memory and $O((N^2 + N)/2)$ time. The comba-squaring method is used only if it can be safely used -without losing carry digits. After 80 digits a Karatsuba squaring algorithm is used whcih requires approximately -$O(4 \cdot N)$ memory and $O(N^{lg(3)})$ time. - -\subsubsection{mp\_div(mp\_int *a, mp\_int *b, mp\_int *c, mp\_int *d)} -Computes $c = \lfloor a/b \rfloor$ and $d \equiv a \mbox{ (mod }b\mbox{)}$. The division is signed which means the sign -of the output is not always positive. The sign of the remainder equals the sign of $a$ while the sign of the -quotient equals the product of the ratios $(a/\vert a \vert) \cdot (b/\vert b \vert)$. Both $c$ and $d$ can be -optionally passed as NULL if the value is not desired. For example, if you want only the quotient of $x/y$ then -mp\_div(\&x, \&y, \&z, NULL) is acceptable. - -This function requires $O(4 \cdot N)$ memory and $O(3 \cdot N^2)$ time. - -\subsubsection{mp\_mod(mp\_int *a, mp\_int *b, mp\_int *c)} -Computes $c \equiv a \mbox{ (mod }b\mbox{)}$ but with the added condition that $0 \le c < b$. That is a normal -division is performed and if the remainder is negative $b$ is added to it. Since adding $b$ modulo $b$ is equivalent -to adding zero ($0 \equiv b \mbox{ (mod }b\mbox{)}$) the result is accurate. The results are undefined -when $b \le 0$, in theory the routine will still give a properly congruent answer but it will not always be positive. - -This function requires $O(4 \cdot N)$ memory and $O(3 \cdot N^2)$ time. - -\subsection{Number Theoretic Functions} - -\subsubsection{mp\_addmod, mp\_submod, mp\_mulmod, mp\_sqrmod} -These functions take the time of their host function plus the time it takes to perform a division. For example, -mp\_addmod takes $O(N + 3 \cdot N^2)$ time. Note that if you are performing many modular operations in a row with -the same modulus you should consider Barrett reductions. - -Also note that these functions use mp\_mod which means the result are guaranteed to be positive. - -\subsubsection{mp\_invmod(mp\_int *a, mp\_int *b, mp\_int *c)} -This function will find $c = 1/a \mbox{ (mod }b\mbox{)}$ for any value of $a$ such that $(a, b) = 1$ and $b > 0$. When -$b$ is odd a ``fast'' variant is used which finds the inverse twice as fast. - -\subsubsection{mp\_gcd(mp\_int *a, mp\_int *b, mp\_int *c)} -Finds the greatest common divisor of both $a$ and $b$ and places the result in $c$. Will work with either positive -or negative inputs. - -Functions requires no additional memory and approximately $O(N \cdot log(N))$ time. - -\subsubsection{mp\_lcm(mp\_int *a, mp\_int *b, mp\_int *c)} -Finds the least common multiple of both $a$ and $b$ and places the result in $c$. Will work with either positive -or negative inputs. This is calculated by dividing the product of $a$ and $b$ by the greatest common divisor of -both. - -Functions requires no additional memory and approximately $O(4 \cdot N^2)$ time. - -\subsubsection{mp\_n\_root(mp\_int *a, mp\_digit b, mp\_int c)} -Finds the $b$'th root of $a$ and stores it in $b$. The roots are found such that $\vert c \vert^b \le \vert a \vert$. -Uses the Newton approximation approach which means it converges in $O(log \beta^N)$ time to a final result. Each iteration -requires $b$ multiplications and one division for a total work of $O(6N^2 \cdot log \beta^N) = O(6N^3 \cdot log \beta)$. - -If the input $a$ is negative and $b$ is even the function returns an error. Otherwise the function will return a root -that has a sign that agrees with the sign of $a$. - -\subsubsection{mp\_jacobi(mp\_int *a, mp\_int *n, int *c)} -Computes $c = \left ( {a \over n} \right )$ or the Jacobi function of $(a, n)$ and stores the result in an integer addressed -by $c$. Since the result of the Jacobi function $\left ( {a \over n} \right ) \in \lbrace -1, 0, 1 \rbrace$ it seemed -natural to store the result in a simple C style \textbf{int}. If $n$ is prime then the Jacobi function produces -the same results as the Legendre function\footnote{Source: Handbook of Applied Cryptography, pp. 73}. This means if -$n$ is prime then $\left ( {a \over n} \right )$ is equal to $1$ if $a$ is a quadratic residue modulo $n$ or $-1$ if -it is not. - -\subsubsection{mp\_exptmod(mp\_int *a, mp\_int *b, mp\_int *c, mp\_int *d)} -Computes $d = a^b \mbox{ (mod }c\mbox{)}$ using a sliding window $k$-ary exponentiation algorithm. For an $\alpha$-bit -exponent it performs $\alpha$ squarings and at most $\lfloor \alpha/k \rfloor + k$ multiplications. The value of $k$ is -chosen to minimize the number of multiplications required for a given value of $\alpha$. Barrett or Montgomery -reductions are used to reduce the squared or multiplied temporary results modulo $c$. - -\subsection{Fast Modular Reductions} - -\subsubsection{mp\_reduce(mp\_int *a, mp\_int *b, mp\_int *c)} -Computes a Barrett reduction in-place of $a$ modulo $b$ with respect to $c$. In essence it computes -$a \equiv a \mbox{ (mod }b\mbox{)}$ provided $0 \le a \le b^2$. The value of $c$ is precomputed with the -function mp\_reduce\_setup(). - -The Barrett reduction function has been optimized to use partial multipliers which means compared to MPI it performs -have the number of single precision multipliers (\textit{provided they have the same size digits}). The partial -multipliers (\textit{one of which is shared with mp\_mul}) both have baseline and comba variants. Barrett reduction -can reduce a number modulo a $n-$digit modulus with approximately $2n^2$ single precision multiplications. - -\subsubsection{mp\_montgomery\_reduce(mp\_int *a, mp\_int *m, mp\_digit mp)} -Computes a Montgomery reduction in-place of $a$ modulo $b$ with respect to $mp$. If $b$ is some $n-$digit modulus then -$R = \beta^{n+1}$. The result of this function is $aR^{-1} \mbox{ (mod }b\mbox{)}$ provided that $0 \le a \le b^2$. -The value of $mp$ is precomputed with the function mp\_montgomery\_setup(). - -The Montgomery reduction comes in two variants. A standard baseline and a fast comba method. The baseline routine -is in fact slower than the Barrett reductions, however, the comba routine is much faster. Montomgery reduction can -reduce a number modulo a $n-$digit modulus with approximately $n^2 + n$ single precision multiplications. - -Note that the final result of a Montgomery reduction is not just the value reduced modulo $b$. You have to multiply -by $R$ modulo $b$ to get the real result. At first that may not seem like such a worthwhile routine, however, the -exptmod function can be made to take advantage of this such that only one normalization at the end is required. - -\section{Timing Analysis} -\subsection{Observed Timings} -A simple test program ``demo.c'' was developed which builds with either MPI or LibTomMath (without modification). The -test was conducted on an AMD Athlon XP processor with 266Mhz DDR memory and the GCC 3.2 compiler\footnote{With build -options ``-O3 -fomit-frame-pointer -funroll-loops''}. The multiplications and squarings were repeated 100,000 times -each while the modular exponentiation (exptmod) were performed 50 times each. The ``inversions'' refers to multiplicative -inversions modulo an odd number of a given size. The RDTSC (Read Time Stamp Counter) instruction was used to measure the -time the entire iterations took and was divided by the number of iterations to get an average. The following results -were observed. - -\begin{small} -\begin{center} -\begin{tabular}{c|c|c|c} -\hline \textbf{Operation} & \textbf{Size (bits)} & \textbf{Time with MPI (cycles)} & \textbf{Time with LibTomMath (cycles)} \\ -\hline -Inversion & 128 & 264,083 & 59,782 \\ -Inversion & 256 & 549,370 & 146,915 \\ -Inversion & 512 & 1,675,975 & 367,172 \\ -Inversion & 1024 & 5,237,957 & 1,054,158 \\ -Inversion & 2048 & 17,871,944 & 3,459,683 \\ -Inversion & 4096 & 66,610,468 & 11,834,556 \\ -\hline -Multiply & 128 & 1,426 & 451 \\ -Multiply & 256 & 2,551 & 958 \\ -Multiply & 512 & 7,913 & 2,476 \\ -Multiply & 1024 & 28,496 & 7,927 \\ -Multiply & 2048 & 109,897 & 28,224 \\ -Multiply & 4096 & 469,970 & 101,171 \\ -\hline -Square & 128 & 1,319 & 511 \\ -Square & 256 & 1,776 & 947 \\ -Square & 512 & 5,399 & 2,153 \\ -Square & 1024 & 18,991 & 5,733 \\ -Square & 2048 & 72,126 & 17,621 \\ -Square & 4096 & 306,269 & 67,576 \\ -\hline -Exptmod & 512 & 32,021,586 & 3,118,435 \\ -Exptmod & 768 & 97,595,492 & 8,493,633 \\ -Exptmod & 1024 & 223,302,532 & 17,715,899 \\ -Exptmod & 2048 & 1,682,223,369 & 114,936,361 \\ -Exptmod & 2560 & 3,268,615,571 & 229,402,426 \\ -Exptmod & 3072 & 5,597,240,141 & 367,403,840 \\ -Exptmod & 4096 & 13,347,270,891 & 779,058,433 - -\end{tabular} -\end{center} -\end{small} - -Note that the figures do fluctuate but their magnitudes are relatively intact. The purpose of the chart is not to -get an exact timing but to compare the two libraries. For example, in all of the tests the exact time for a 512-bit -squaring operation was not the same. The observed times were all approximately 2,500 cycles, more importantly they -were always faster than the timings observed with MPI by about the same magnitude. - -\subsection{Digit Size} -The first major constribution to the time savings is the fact that 28 bits are stored per digit instead of the MPI -defualt of 16. This means in many of the algorithms the savings can be considerable. Consider a baseline multiplier -with a 1024-bit input. With MPI the input would be 64 16-bit digits whereas in LibTomMath it would be 37 28-bit digits. -A savings of $64^2 - 37^2 = 2727$ single precision multiplications. - -\subsection{Multiplication Algorithms} -For most inputs a typical baseline $O(n^2)$ multiplier is used which is similar to that of MPI. There are two variants -of the baseline multiplier. The normal and the fast variants. The normal baseline multiplier is the exact same as the -algorithm from MPI. The fast baseline multiplier is optimized for cases where the number of input digits $N$ is less -than or equal to $2^{w}/\beta^2$. Where $w$ is the number of bits in a \textbf{mp\_word}. By default a mp\_word is -64-bits which means $N \le 256$ is allowed which represents numbers upto $7168$ bits. - -The fast baseline multiplier is optimized by removing the carry operations from the inner loop. This is often referred -to as the ``comba'' method since it computes the products a columns first then figures out the carries. This has the -effect of making a very simple and paralizable inner loop. - -For large inputs, typically 80 digits\footnote{By default that is 2240-bits or more.} or more the Karatsuba method is -used. This method has significant overhead but an asymptotic running time of $O(n^{1.584})$ which means for fairly large -inputs this method is faster. The Karatsuba implementation is recursive which means for extremely large inputs they -will benefit from the algorithm. - -MPI only implements the slower baseline multiplier where carries are dealt with in the inner loop. As a result even at -smaller numbers (below the Karatsuba cutoff) the LibTomMath multipliers are faster. - -\subsection{Squaring Algorithms} - -Similar to the multiplication algorithms there are two baseline squaring algorithms. Both have an asymptotic running -time of $O((t^2 + t)/2)$. The normal baseline squaring is the same from MPI and the fast is a ``comba'' squaring -algorithm. The comba method is used if the number of digits $N$ is less than $2^{w-1}/\beta^2$ which by default -covers numbers upto $3584$ bits. - -There is also a Karatsuba squaring method which achieves a running time of $O(n^{1.584})$ after considerably large -inputs. - -MPI only implements the slower baseline squaring algorithm. As a result LibTomMath is considerably faster at squaring -than MPI is. - -\subsection{Exponentiation Algorithms} - -LibTomMath implements a sliding window $k$-ary left to right exponentiation algorithm. For a given exponent size $L$ an -appropriate window size $k$ is chosen. There are always at most $L$ modular squarings and $\lfloor L/k \rfloor$ modular -multiplications. The $k$-ary method works by precomputing values $g(x) = b^x$ for $0 \le x < 2^k$ and a given base -$b$. Then the multiplications are grouped in windows of $k$ bits. The sliding window technique has the benefit -that it can skip multiplications if there are zero bits following or preceding a window. Consider the exponent -$e = 11110001_2$ if $k = 2$ then there will be a two squarings, a multiplication of $g(3)$, two squarings, a multiplication -of $g(3)$, four squarings and and a multiplication by $g(1)$. In total there are 8 squarings and 3 multiplications. - -MPI uses a binary square-multiply method. For the same exponent $e$ it would have had 8 squarings and 5 multiplications. -There is a precomputation phase for the method LibTomMath uses but it generally cuts down considerably on the number -of multiplications. Consider a 512-bit exponent. The worst case for the LibTomMath method results in 512 squarings and -124 multiplications. The MPI method would have 512 squarings and 512 multiplications. Randomly every $2k$ bits another -multiplication is saved via the sliding-window technique on top of the savings the $k$-ary method provides. - -Both LibTomMath and MPI use Barrett reduction instead of division to reduce the numbers modulo the modulus given. -However, LibTomMath can take advantage of the fact that the multiplications required within the Barrett reduction -do not have to give full precision. As a result the reduction step is much faster and just as accurate. The LibTomMath code -will automatically determine at run-time (e.g. when its called) whether the faster multiplier can be used. The -faster multipliers have also been optimized into the two variants (baseline and comba baseline). - -As a result of all these changes exponentiation in LibTomMath is much faster than compared to MPI. - - - -\end{document} \ No newline at end of file +\documentclass{article} +\begin{document} + +\title{LibTomMath v0.11 \\ A Free Multiple Precision Integer Library} +\author{Tom St Denis \\ tomstdenis@iahu.ca} +\maketitle +\newpage + +\section{Introduction} +``LibTomMath'' is a free and open source library that provides multiple-precision integer functions required to form a basis +of a public key cryptosystem. LibTomMath is written entire in portable ISO C source code and designed to have an application +interface much like that of MPI from Michael Fromberger. + +LibTomMath was written from scratch by Tom St Denis but designed to be drop in replacement for the MPI package. The +algorithms within the library are derived from descriptions as provided in the Handbook of Applied Cryptography and Knuth's +``The Art of Computer Programming''. The library has been extensively optimized and should provide quite comparable +timings as compared to many free and commercial libraries. + +LibTomMath was designed with the following goals in mind: +\begin{enumerate} +\item Be a drop in replacement for MPI. +\item Be much faster than MPI. +\item Be written entirely in portable C. +\end{enumerate} + +All three goals have been achieved. Particularly the speed increase goal. For example, a 512-bit modular exponentiation +is eight times faster\footnote{On an Athlon XP with GCC 3.2} with LibTomMath compared to MPI. + +Being compatible with MPI means that applications that already use it can be ported fairly quickly. Currently there are +a few differences but there are many similarities. In fact the average MPI based application can be ported in under 15 +minutes. + +Thanks goes to Michael Fromberger for answering a couple questions and Colin Percival for having the patience and courtesy to +help debug and suggest optimizations. They were both of great help! + +\section{Building Against LibTomMath} + +Building against LibTomMath is very simple because there is only one source file. Simply add ``bn.c'' to your project and +copy both ``bn.c'' and ``bn.h'' into your project directory. There is no configuration nor building required before hand. + +If you are porting an MPI application to LibTomMath the first step will be to remove all references to MPI and replace them +with references to LibTomMath. For example, substitute + +\begin{verbatim} +#include "mpi.h" +\end{verbatim} + +with + +\begin{verbatim} +#include "bn.h" +\end{verbatim} + +Remove ``mpi.c'' from your project and replace it with ``bn.c''. + +\section{Programming with LibTomMath} + +\subsection{The mp\_int Structure} +All multiple precision integers are stored in a structure called \textbf{mp\_int}. A multiple precision integer is +essentially an array of \textbf{mp\_digit}. mp\_digit is defined at the top of bn.h. Its type can be changed to suit +a particular platform. + +For example, when \textbf{MP\_8BIT} is defined\footnote{When building bn.c.} a mp\_digit is a unsigned char and holds +seven bits. Similarly when \textbf{MP\_16BIT} is defined a mp\_digit is a unsigned short and holds 15 bits. +By default a mp\_digit is a unsigned long and holds 28 bits. + +The choice of digit is particular to the platform at hand and what available multipliers are provided. For +MP\_8BIT either a $8 \times 8 \Rightarrow 16$ or $16 \times 16 \Rightarrow 16$ multiplier is optimal. When +MP\_16BIT is defined either a $16 \times 16 \Rightarrow 32$ or $32 \times 32 \Rightarrow 32$ multiplier is optimal. By +default a $32 \times 32 \Rightarrow 64$ or $64 \times 64 \Rightarrow 64$ multiplier is optimal. + +This gives the library some flexibility. For example, a i8051 has a $8 \times 8 \Rightarrow 16$ multiplier. The +16-bit x86 instruction set has a $16 \times 16 \Rightarrow 32$ multiplier. In practice this library is not particularly +designed for small devices like an i8051 due to the size. It is possible to strip out functions which are not required +to drop the code size. More realistically the library is well suited to 32 and 64-bit processors that have decent +integer multipliers. The AMD Athlon XP and Intel Pentium 4 processors are examples of well suited processors. + +Throughout the discussions there will be references to a \textbf{used} and \textbf{alloc} members of an integer. The +used member refers to how many digits are actually used in the representation of the integer. The alloc member refers +to how many digits have been allocated off the heap. There is also the $\beta$ quantity which is equal to $2^W$ where +$W$ is the number of bits in a digit (default is 28). + +\subsection{Calling Functions} +Most functions expect pointers to mp\_int's as parameters. To save on memory usage it is possible to have source +variables as destinations. For example: +\begin{verbatim} + mp_add(&x, &y, &x); /* x = x + y */ + mp_mul(&x, &z, &x); /* x = x * z */ + mp_div_2(&x, &x); /* x = x / 2 */ +\end{verbatim} + +\section{Quick Overview} + +\subsection{Basic Functionality} +Essentially all LibTomMath functions return one of three values to indicate if the function worked as desired. A +function will return \textbf{MP\_OKAY} if the function was successful. A function will return \textbf{MP\_MEM} if +it ran out of memory and \textbf{MP\_VAL} if the input was invalid. + +Before an mp\_int can be used it must be initialized with + +\begin{verbatim} +int mp_init(mp_int *a); +\end{verbatim} + +For example, consider the following. + +\begin{verbatim} +#include "bn.h" +int main(void) +{ + mp_int num; + if (mp_init(&num) != MP_OKAY) { + printf("Error initializing a mp_int.\n"); + } + return 0; +} +\end{verbatim} + +A mp\_int can be freed from memory with + +\begin{verbatim} +void mp_clear(mp_int *a); +\end{verbatim} + +This will zero the memory and free the allocated data. There are a set of trivial functions to manipulate the +value of an mp\_int. + +\begin{verbatim} +/* set to zero */ +void mp_zero(mp_int *a); + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b); + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b); + +/* init to a given number of digits */ +int mp_init_size(mp_int *a, int size); + +/* copy, b = a */ +int mp_copy(mp_int *a, mp_int *b); + +/* inits and copies, a = b */ +int mp_init_copy(mp_int *a, mp_int *b); +\end{verbatim} + +The \textbf{mp\_zero} function will clear the contents of a mp\_int and set it to positive. The \textbf{mp\_set} function +will zero the integer and set the first digit to a value specified. The \textbf{mp\_set\_int} function will zero the +integer and set the first 32-bits to a given value. It is important to note that using mp\_set can have unintended +side effects when either the MP\_8BIT or MP\_16BIT defines are enabled. By default the library will accept the +ranges of values MPI will (and more). + +The \textbf{mp\_init\_size} function will initialize the integer and set the allocated size to a given value. The +allocated digits are zero'ed by default but not marked as used. The \textbf{mp\_copy} function will copy the digits +(and sign) of the first parameter into the integer specified by the second parameter. The \textbf{mp\_init\_copy} will +initialize the first integer specified and copy the second one into it. Note that the order is reversed from that of +mp\_copy. This odd ``bug'' was kept to maintain compatibility with MPI. + +\subsection{Digit Manipulations} + +There are a class of functions that provide simple digit manipulations such as shifting and modulo reduction of powers +of two. + +\begin{verbatim} +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +int mp_lshd(mp_int *a, int b); + +/* c = a / 2^b */ +int mp_div_2d(mp_int *a, int b, mp_int *c); + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b); + +/* c = a * 2^b */ +int mp_mul_2d(mp_int *a, int b, mp_int *c); + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b); + +/* c = a mod 2^d */ +int mp_mod_2d(mp_int *a, int b, mp_int *c); +\end{verbatim} + +\subsection{Basic Arithmetic} + +Next are the class of functions which provide basic arithmetic. + +\begin{verbatim} +/* b = -a */ +int mp_neg(mp_int *a, mp_int *b); + +/* b = |a| */ +int mp_abs(mp_int *a, mp_int *b); + +/* compare a to b */ +int mp_cmp(mp_int *a, mp_int *b); + +/* compare |a| to |b| */ +int mp_cmp_mag(mp_int *a, mp_int *b); + +/* c = a + b */ +int mp_add(mp_int *a, mp_int *b, mp_int *c); + +/* c = a - b */ +int mp_sub(mp_int *a, mp_int *b, mp_int *c); + +/* c = a * b */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c); + +/* b = a^2 */ +int mp_sqr(mp_int *a, mp_int *b); + +/* a/b => cb + d == a */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a mod b, 0 <= c < b */ +int mp_mod(mp_int *a, mp_int *b, mp_int *c); +\end{verbatim} + +\subsection{Single Digit Functions} + +\begin{verbatim} +/* compare against a single digit */ +int mp_cmp_d(mp_int *a, mp_digit b); + +/* c = a + b */ +int mp_add_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a - b */ +int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a * b */ +int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); + +/* a/b => cb + d == a */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); + +/* c = a mod b, 0 <= c < b */ +int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); +\end{verbatim} + +Note that care should be taken for the value of the digit passed. By default, any 28-bit integer is a valid digit that can +be passed into the function. However, if MP\_8BIT or MP\_16BIT is defined only 7 or 15-bit (respectively) integers +can be passed into it. + +\subsection{Modular Arithmetic} + +There are some trivial modular arithmetic functions. + +\begin{verbatim} +/* d = a + b (mod c) */ +int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a - b (mod c) */ +int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a * a (mod b) */ +int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = 1/a (mod b) */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = (a, b) */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c); + +/* c = [a, b] or (a*b)/(a, b) */ +int mp_lcm(mp_int *a, mp_int *b, mp_int *c); + +/* find the b'th root of a */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c); + +/* computes the jacobi c = (a | n) (or Legendre if b is prime) */ +int mp_jacobi(mp_int *a, mp_int *n, int *c); + +/* used to setup the Barrett reduction for a given modulus b */ +int mp_reduce_setup(mp_int *a, mp_int *b); + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < a <= b^2, note if 0 > a > -(b^2) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. + */ +int mp_reduce(mp_int *a, mp_int *b, mp_int *c); + +/* setups the montgomery reduction */ +int mp_montgomery_setup(mp_int *a, mp_digit *mp); + +/* computes xR^-1 == x (mod N) via Montgomery Reduction */ +int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); + +/* d = a^b (mod c) */ +int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); +\end{verbatim} + +\subsection{Radix Conversions} +To read or store integers in other formats there are the following functions. + +\begin{verbatim} +int mp_unsigned_bin_size(mp_int *a); +int mp_read_unsigned_bin(mp_int *a, unsigned char *b, int c); +int mp_to_unsigned_bin(mp_int *a, unsigned char *b); + +int mp_signed_bin_size(mp_int *a); +int mp_read_signed_bin(mp_int *a, unsigned char *b, int c); +int mp_to_signed_bin(mp_int *a, unsigned char *b); + +int mp_read_radix(mp_int *a, unsigned char *str, int radix); +int mp_toradix(mp_int *a, unsigned char *str, int radix); +int mp_radix_size(mp_int *a, int radix); +\end{verbatim} + +The integers are stored in big endian format as most libraries (and MPI) expect. The \textbf{mp\_read\_radix} and +\textbf{mp\_toradix} functions read and write (respectively) null terminated ASCII strings in a given radix. Valid values +for the radix are between 2 and 64 (inclusively). + +\section{Function Analysis} + +Throughout the function analysis the variable $N$ will denote the average size of an input to a function as measured +by the number of digits it has. The variable $W$ will denote the number of bits per word and $c$ will denote a small +constant amount of work. The big-oh notation will be abused slightly to consider numbers that do not grow to infinity. +That is we shall consider $O(N/2) \ne O(N)$ which is an abuse of the notation. + +\subsection{Digit Manipulation Functions} +The class of digit manipulation functions such as \textbf{mp\_rshd}, \textbf{mp\_lshd} and \textbf{mp\_mul\_2} are all +very simple functions to analyze. + +\subsubsection{mp\_rshd(mp\_int *a, int b)} +Shifts $a$ by given number of digits to the right and is equivalent to dividing by $\beta^b$. The work is performed +in-place which means the input and output are the same. If the shift count $b$ is less than or equal to zero +the function returns without doing any work. If the the shift count is larger than the number of digits in $a$ +then $a$ is simply zeroed without shifting digits. + +This function requires no additional memory and $O(N)$ time. + +\subsubsection{mp\_lshd(mp\_int *a, int b)} +Shifts $a$ by a given number of digits to the left and is equivalent to multiplying by $\beta^b$. The work +is performed in-place which means the input and output are the same. If the shift count $b$ is less than or equal +to zero the function returns success without doing any work. + +This function requires $O(b)$ additional digits of memory and $O(N)$ time. + +\subsubsection{mp\_div\_2d(mp\_int *a, int b, mp\_int *c, mp\_int *d)} +Shifts $a$ by a given number of \textbf{bits} to the right and is equivalent to dividing by $2^b$. The shifted number is stored +in the $c$ parameter. The remainder of $a/2^b$ is optionally stored in $d$ (if it is not passed as NULL). +If the shift count $b$ is less than or equal to zero the function places $a$ in $c$ and returns success. + +This function requires $O(2 \cdot N)$ additional digits of memory and $O(2 \cdot N)$ time. + +\subsubsection{mp\_mul\_2d(mp\_int *a, int b, mp\_int *c)} +Shifts $a$ by a given number of bits to the left and is equivalent to multiplying by $2^b$. The shifted number +is placed in the $c$ parameter. If the shift count $b$ is less than or equal to zero the function places $a$ +in $c$ and returns success. + +This function requires $O(N)$ additional digits of memory and $O(2 \cdot N)$ time. + +\subsubsection{mp\_mod\_2d(mp\_int *a, int b, mp\_int *c)} +Performs the action of reducing $a$ modulo $2^b$ and stores the result in $c$. If the shift count $b$ is less than +or equal to zero the function places $a$ in $c$ and returns success. + +This function requires $O(N)$ additional digits of memory and $O(2 \cdot N)$ time. + +\subsection{Basic Arithmetic} + +\subsubsection{mp\_cmp(mp\_int *a, mp\_int *b)} +Performs a \textbf{signed} comparison between $a$ and $b$ returning \textbf{MP\_GT} is $a$ is larger than $b$. + +This function requires no additional memory and $O(N)$ time. + +\subsubsection{mp\_cmp\_mag(mp\_int *a, mp\_int *b)} +Performs a \textbf{unsigned} comparison between $a$ and $b$ returning \textbf{MP\_GT} is $a$ is larger than $b$. Note +that this comparison is unsigned which means it will report, for example, $-5 > 3$. By comparison mp\_cmp will +report $-5 < 3$. + +This function requires no additional memory and $O(N)$ time. + +\subsubsection{mp\_add(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes $c = a + b$ using signed arithmetic. Handles the sign of the numbers which means it will subtract as +required, e.g. $a + -b$ turns into $a - b$. + +This function requires no additional memory and $O(N)$ time. + +\subsubsection{mp\_sub(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes $c = a - b$ using signed arithmetic. Handles the sign of the numbers which means it will add as +required, e.g. $a - -b$ turns into $a + b$. + +This function requires no additional memory and $O(N)$ time. + +\subsubsection{mp\_mul(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes $c = a \cdot b$ using signed arithmetic. Handles the sign of the numbers correctly which means it will +correct the sign of the product as required, e.g. $a \cdot -b$ turns into $-ab$. + +For relatively small inputs, that is less than 80 digits a standard baseline or comba-baseline multiplier is used. It +requires no additional memory and $O(N^2)$ time. The comba-baseline multiplier is only used if it can safely be used +without losing carry digits. The comba method is faster than the baseline method but cannot always be used which is why +both are provided. The code will automatically determine when it can be used. If the digit count is higher +than 80 for the inputs than a Karatsuba multiplier is used which requires approximately $O(6 \cdot N)$ memory and +$O(N^{lg(3)})$ time. + +\subsubsection{mp\_sqr(mp\_int *a, mp\_int *b)} +Computes $b = a^2$. +For relatively small inputs, that is less than 80 digits a modified squaring or comba-squaring algorithm is used. It +requires no additional memory and $O((N^2 + N)/2)$ time. The comba-squaring method is used only if it can be safely used +without losing carry digits. After 80 digits a Karatsuba squaring algorithm is used whcih requires approximately +$O(4 \cdot N)$ memory and $O(N^{lg(3)})$ time. + +\subsubsection{mp\_div(mp\_int *a, mp\_int *b, mp\_int *c, mp\_int *d)} +Computes $c = \lfloor a/b \rfloor$ and $d \equiv a \mbox{ (mod }b\mbox{)}$. The division is signed which means the sign +of the output is not always positive. The sign of the remainder equals the sign of $a$ while the sign of the +quotient equals the product of the ratios $(a/\vert a \vert) \cdot (b/\vert b \vert)$. Both $c$ and $d$ can be +optionally passed as NULL if the value is not desired. For example, if you want only the quotient of $x/y$ then +mp\_div(\&x, \&y, \&z, NULL) is acceptable. + +This function requires $O(4 \cdot N)$ memory and $O(3 \cdot N^2)$ time. + +\subsubsection{mp\_mod(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes $c \equiv a \mbox{ (mod }b\mbox{)}$ but with the added condition that $0 \le c < b$. That is a normal +division is performed and if the remainder is negative $b$ is added to it. Since adding $b$ modulo $b$ is equivalent +to adding zero ($0 \equiv b \mbox{ (mod }b\mbox{)}$) the result is accurate. The results are undefined +when $b \le 0$, in theory the routine will still give a properly congruent answer but it will not always be positive. + +This function requires $O(4 \cdot N)$ memory and $O(3 \cdot N^2)$ time. + +\subsection{Number Theoretic Functions} + +\subsubsection{mp\_addmod, mp\_submod, mp\_mulmod, mp\_sqrmod} +These functions take the time of their host function plus the time it takes to perform a division. For example, +mp\_addmod takes $O(N + 3 \cdot N^2)$ time. Note that if you are performing many modular operations in a row with +the same modulus you should consider Barrett reductions. + +Also note that these functions use mp\_mod which means the result are guaranteed to be positive. + +\subsubsection{mp\_invmod(mp\_int *a, mp\_int *b, mp\_int *c)} +This function will find $c = 1/a \mbox{ (mod }b\mbox{)}$ for any value of $a$ such that $(a, b) = 1$ and $b > 0$. When +$b$ is odd a ``fast'' variant is used which finds the inverse twice as fast. + +\subsubsection{mp\_gcd(mp\_int *a, mp\_int *b, mp\_int *c)} +Finds the greatest common divisor of both $a$ and $b$ and places the result in $c$. Will work with either positive +or negative inputs. + +Functions requires no additional memory and approximately $O(N \cdot log(N))$ time. + +\subsubsection{mp\_lcm(mp\_int *a, mp\_int *b, mp\_int *c)} +Finds the least common multiple of both $a$ and $b$ and places the result in $c$. Will work with either positive +or negative inputs. This is calculated by dividing the product of $a$ and $b$ by the greatest common divisor of +both. + +Functions requires no additional memory and approximately $O(4 \cdot N^2)$ time. + +\subsubsection{mp\_n\_root(mp\_int *a, mp\_digit b, mp\_int c)} +Finds the $b$'th root of $a$ and stores it in $b$. The roots are found such that $\vert c \vert^b \le \vert a \vert$. +Uses the Newton approximation approach which means it converges in $O(log \beta^N)$ time to a final result. Each iteration +requires $b$ multiplications and one division for a total work of $O(6N^2 \cdot log \beta^N) = O(6N^3 \cdot log \beta)$. + +If the input $a$ is negative and $b$ is even the function returns an error. Otherwise the function will return a root +that has a sign that agrees with the sign of $a$. + +\subsubsection{mp\_jacobi(mp\_int *a, mp\_int *n, int *c)} +Computes $c = \left ( {a \over n} \right )$ or the Jacobi function of $(a, n)$ and stores the result in an integer addressed +by $c$. Since the result of the Jacobi function $\left ( {a \over n} \right ) \in \lbrace -1, 0, 1 \rbrace$ it seemed +natural to store the result in a simple C style \textbf{int}. If $n$ is prime then the Jacobi function produces +the same results as the Legendre function\footnote{Source: Handbook of Applied Cryptography, pp. 73}. This means if +$n$ is prime then $\left ( {a \over n} \right )$ is equal to $1$ if $a$ is a quadratic residue modulo $n$ or $-1$ if +it is not. + +\subsubsection{mp\_exptmod(mp\_int *a, mp\_int *b, mp\_int *c, mp\_int *d)} +Computes $d = a^b \mbox{ (mod }c\mbox{)}$ using a sliding window $k$-ary exponentiation algorithm. For an $\alpha$-bit +exponent it performs $\alpha$ squarings and at most $\lfloor \alpha/k \rfloor + 2^{k-1}$ multiplications. The value of $k$ is +chosen to minimize the number of multiplications required for a given value of $\alpha$. Barrett or Montgomery +reductions are used to reduce the squared or multiplied temporary results modulo $c$. + +\subsection{Fast Modular Reductions} + +\subsubsection{mp\_reduce(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes a Barrett reduction in-place of $a$ modulo $b$ with respect to $c$. In essence it computes +$a \equiv a \mbox{ (mod }b\mbox{)}$ provided $0 \le a \le b^2$. The value of $c$ is precomputed with the +function mp\_reduce\_setup(). The modulus $b$ must be larger than zero. + +The Barrett reduction function has been optimized to use partial multipliers which means compared to MPI it performs +have the number of single precision multipliers (\textit{provided they have the same size digits}). The partial +multipliers (\textit{one of which is shared with mp\_mul}) both have baseline and comba variants. Barrett reduction +can reduce a number modulo a $n-$digit modulus with approximately $2n^2$ single precision multiplications. + +\subsubsection{mp\_montgomery\_reduce(mp\_int *a, mp\_int *m, mp\_digit mp)} +Computes a Montgomery reduction in-place of $a$ modulo $b$ with respect to $mp$. If $b$ is some $n-$digit modulus then +$R = \beta^{n+1}$. The result of this function is $aR^{-1} \mbox{ (mod }b\mbox{)}$ provided that $0 \le a \le b^2$. +The value of $mp$ is precomputed with the function mp\_montgomery\_setup(). The modulus $b$ must be odd and larger +than zero. + +The Montgomery reduction comes in two variants. A standard baseline and a fast comba method. The baseline routine +is in fact slower than the Barrett reductions, however, the comba routine is much faster. Montomgery reduction can +reduce a number modulo a $n-$digit modulus with approximately $n^2 + n$ single precision multiplications. Compared +to Barrett reductions the montgomery reduction requires half as many multiplications as $n \rightarrow \infty$. + +Note that the final result of a Montgomery reduction is not just the value reduced modulo $b$. You have to multiply +by $R$ modulo $b$ to get the real result. At first that may not seem like such a worthwhile routine, however, the +exptmod function can be made to take advantage of this such that only one normalization at the end is required. + +This stems from the fact that if $a \rightarrow aR^{-1}$ through Montgomery reduction and if $a = vR$ and $b = uR$ then +$a^2 \rightarrow v^2R^2R^{-1} \equiv v^2R$ and $ab \rightarrow uvRRR^{-1} \equiv uvR$. The next useful observation is +that through the reduction $a \rightarrow vRR^{-1} \equiv v$ which means given a final result it can be normalized with +a single reduction. Now a series of complicated modular operations can be optimized if all the variables are initially +multiplied by $R$ then the final result normalized by performing an extra reduction. + +If many variables are to be normalized the simplest method to setup the variables is to first compute $\hat x \equiv R^2 \mbox{ mod }m$. +Now all the variables in the system can be multiplied by $\hat x$ and reduced with Montgomery reduction. This means that +two long divisions would be required to setup $\hat x$ and a multiplication followed by reduction for each variable. + +A very useful observation is that multiplying by $R = \beta^n$ amounts to performing a left shift by $n$ positions which +requires no single precision multiplications. + +\section{Timing Analysis} +\subsection{Observed Timings} +A simple test program ``demo.c'' was developed which builds with either MPI or LibTomMath (without modification). The +test was conducted on an AMD Athlon XP processor with 266Mhz DDR memory and the GCC 3.2 compiler\footnote{With build +options ``-O3 -fomit-frame-pointer -funroll-loops''}. The multiplications and squarings were repeated 100,000 times +each while the modular exponentiation (exptmod) were performed 50 times each. The ``inversions'' refers to multiplicative +inversions modulo an odd number of a given size. The RDTSC (Read Time Stamp Counter) instruction was used to measure the +time the entire iterations took and was divided by the number of iterations to get an average. The following results +were observed. + +\begin{small} +\begin{center} +\begin{tabular}{c|c|c|c} +\hline \textbf{Operation} & \textbf{Size (bits)} & \textbf{Time with MPI (cycles)} & \textbf{Time with LibTomMath (cycles)} \\ +\hline +Inversion & 128 & 264,083 & 59,782 \\ +Inversion & 256 & 549,370 & 146,915 \\ +Inversion & 512 & 1,675,975 & 367,172 \\ +Inversion & 1024 & 5,237,957 & 1,054,158 \\ +Inversion & 2048 & 17,871,944 & 3,459,683 \\ +Inversion & 4096 & 66,610,468 & 11,834,556 \\ +\hline +Multiply & 128 & 1,426 & 451 \\ +Multiply & 256 & 2,551 & 958 \\ +Multiply & 512 & 7,913 & 2,476 \\ +Multiply & 1024 & 28,496 & 7,927 \\ +Multiply & 2048 & 109,897 & 28,224 \\ +Multiply & 4096 & 469,970 & 101,171 \\ +\hline +Square & 128 & 1,319 & 511 \\ +Square & 256 & 1,776 & 947 \\ +Square & 512 & 5,399 & 2,153 \\ +Square & 1024 & 18,991 & 5,733 \\ +Square & 2048 & 72,126 & 17,621 \\ +Square & 4096 & 306,269 & 67,576 \\ +\hline +Exptmod & 512 & 32,021,586 & 3,118,435 \\ +Exptmod & 768 & 97,595,492 & 8,493,633 \\ +Exptmod & 1024 & 223,302,532 & 17,715,899 \\ +Exptmod & 2048 & 1,682,223,369 & 114,936,361 \\ +Exptmod & 2560 & 3,268,615,571 & 229,402,426 \\ +Exptmod & 3072 & 5,597,240,141 & 367,403,840 \\ +Exptmod & 4096 & 13,347,270,891 & 779,058,433 + +\end{tabular} +\end{center} +\end{small} + +Note that the figures do fluctuate but their magnitudes are relatively intact. The purpose of the chart is not to +get an exact timing but to compare the two libraries. For example, in all of the tests the exact time for a 512-bit +squaring operation was not the same. The observed times were all approximately 2,500 cycles, more importantly they +were always faster than the timings observed with MPI by about the same magnitude. + +\subsection{Digit Size} +The first major constribution to the time savings is the fact that 28 bits are stored per digit instead of the MPI +defualt of 16. This means in many of the algorithms the savings can be considerable. Consider a baseline multiplier +with a 1024-bit input. With MPI the input would be 64 16-bit digits whereas in LibTomMath it would be 37 28-bit digits. +A savings of $64^2 - 37^2 = 2727$ single precision multiplications. + +\subsection{Multiplication Algorithms} +For most inputs a typical baseline $O(n^2)$ multiplier is used which is similar to that of MPI. There are two variants +of the baseline multiplier. The normal and the fast variants. The normal baseline multiplier is the exact same as the +algorithm from MPI. The fast baseline multiplier is optimized for cases where the number of input digits $N$ is less +than or equal to $2^{w}/\beta^2$. Where $w$ is the number of bits in a \textbf{mp\_word}. By default a mp\_word is +64-bits which means $N \le 256$ is allowed which represents numbers upto $7168$ bits. + +The fast baseline multiplier is optimized by removing the carry operations from the inner loop. This is often referred +to as the ``comba'' method since it computes the products a columns first then figures out the carries. This has the +effect of making a very simple and paralizable inner loop. + +For large inputs, typically 80 digits\footnote{By default that is 2240-bits or more.} or more the Karatsuba method is +used. This method has significant overhead but an asymptotic running time of $O(n^{1.584})$ which means for fairly large +inputs this method is faster. The Karatsuba implementation is recursive which means for extremely large inputs they +will benefit from the algorithm. + +MPI only implements the slower baseline multiplier where carries are dealt with in the inner loop. As a result even at +smaller numbers (below the Karatsuba cutoff) the LibTomMath multipliers are faster. + +\subsection{Squaring Algorithms} + +Similar to the multiplication algorithms there are two baseline squaring algorithms. Both have an asymptotic running +time of $O((t^2 + t)/2)$. The normal baseline squaring is the same from MPI and the fast is a ``comba'' squaring +algorithm. The comba method is used if the number of digits $N$ is less than $2^{w-1}/\beta^2$ which by default +covers numbers upto $3584$ bits. + +There is also a Karatsuba squaring method which achieves a running time of $O(n^{1.584})$ after considerably large +inputs. + +MPI only implements the slower baseline squaring algorithm. As a result LibTomMath is considerably faster at squaring +than MPI is. + +\subsection{Exponentiation Algorithms} + +LibTomMath implements a sliding window $k$-ary left to right exponentiation algorithm. For a given exponent size $L$ an +appropriate window size $k$ is chosen. There are always at most $L$ modular squarings and $\lfloor L/k \rfloor$ modular +multiplications. The $k$-ary method works by precomputing values $g(x) = b^x$ for $0 \le x < 2^k$ and a given base +$b$. Then the multiplications are grouped in windows of $k$ bits. The sliding window technique has the benefit +that it can skip multiplications if there are zero bits following or preceding a window. Consider the exponent +$e = 11110001_2$ if $k = 2$ then there will be a two squarings, a multiplication of $g(3)$, two squarings, a multiplication +of $g(3)$, four squarings and and a multiplication by $g(1)$. In total there are 8 squarings and 3 multiplications. + +MPI uses a binary square-multiply method. For the same exponent $e$ it would have had 8 squarings and 5 multiplications. +There is a precomputation phase for the method LibTomMath uses but it generally cuts down considerably on the number +of multiplications. Consider a 512-bit exponent. The worst case for the LibTomMath method results in 512 squarings and +124 multiplications. The MPI method would have 512 squarings and 512 multiplications. Randomly every $2k$ bits another +multiplication is saved via the sliding-window technique on top of the savings the $k$-ary method provides. + +Both LibTomMath and MPI use Barrett reduction instead of division to reduce the numbers modulo the modulus given. +However, LibTomMath can take advantage of the fact that the multiplications required within the Barrett reduction +do not have to give full precision. As a result the reduction step is much faster and just as accurate. The LibTomMath code +will automatically determine at run-time (e.g. when its called) whether the faster multiplier can be used. The +faster multipliers have also been optimized into the two variants (baseline and comba baseline). + +As a result of all these changes exponentiation in LibTomMath is much faster than compared to MPI. + + + +\end{document} diff --git a/changes.txt b/changes.txt index d302ee6..e78a2a3 100644 --- a/changes.txt +++ b/changes.txt @@ -1,70 +1,76 @@ -Jan 9th, 2003 -v0.10 -- Pekka Riikonen suggested fixes to the radix conversion code. - -- Added baseline montgomery and comba montgomery reductions, sped up exptmods - [to a point, see bn.h for MONTGOMERY_EXPT_CUTOFF] - -Jan 6th, 2003 -v0.09 -- Updated the manual to reflect recent changes. :-) - -- Added Jacobi function (mp_jacobi) to supplement the number theory side of the lib - -- Added a Mersenne prime finder demo in ./etc/mersenne.c - -Jan 2nd, 2003 -v0.08 -- Sped up the multipliers by moving the inner loop variables into a smaller scope - -- Corrected a bunch of small "warnings" - -- Added more comments - -- Made "mtest" be able to use /dev/random, /dev/urandom or stdin for RNG data - -- Corrected some bugs where error messages were potentially ignored - -- add etc/pprime.c program which makes numbers which are provably prime. - -Jan 1st, 2003 -v0.07 -- Removed alot of heap operations from core functions to speed them up - -- Added a root finding function [and mp_sqrt macro like from MPI] - -- Added more to manual - -Dec 31st, 2002 -v0.06 -- Sped up the s_mp_add, s_mp_sub which inturn sped up mp_invmod, mp_exptmod, etc... - -- Cleaned up the header a bit more - -Dec 30th, 2002 -v0.05 -- Builds with MSVC out of the box - -- Fixed a bug in mp_invmod w.r.t. even moduli - -- Made mp_toradix and mp_read_radix use char instead of unsigned char arrays - -- Fixed up exptmod to use fewer multiplications - -- Fixed up mp_init_size to use only one heap operation - -- Note there is a slight "off-by-one" bug in the library somewhere - without the padding (see the source for comment) the library - crashes in libtomcrypt. Anyways a reasonable workaround is to pad the - numbers which will always correct it since as the numbers grow the padding - will still be beyond the end of the number - -- Added more to the manual - -Dec 29th, 2002 -v0.04 -- Fixed a memory leak in mp_to_unsigned_bin - -- optimized invmod code - -- Fixed bug in mp_div - -- use exchange instead of copy for results - -- added a bit more to the manual - -Dec 27th, 2002 -v0.03 -- Sped up s_mp_mul_high_digs by not computing the carries of the lower digits - -- Fixed a bug where mp_set_int wouldn't zero the value first and set the used member. - -- fixed a bug in s_mp_mul_high_digs where the limit placed on the result digits was not calculated properly - -- fixed bugs in add/sub/mul/sqr_mod functions where if the modulus and dest were the same it wouldn't work - -- fixed a bug in mp_mod and mp_mod_d concerning negative inputs - -- mp_mul_d didn't preserve sign - -- Many many many many fixes - -- Works in LibTomCrypt now :-) - -- Added iterations to the timing demos... more accurate. - -- Tom needs a job. - -Dec 26th, 2002 -v0.02 -- Fixed a few "slips" in the manual. This is "LibTomMath" afterall :-) - -- Added mp_cmp_mag, mp_neg, mp_abs and mp_radix_size that were missing. - -- Sped up the fast [comba] multipliers more [yahoo!] - -Dec 25th,2002 -v0.01 -- Initial release. Gimme a break. - -- Todo list, - add details to manual [e.g. algorithms] - more comments in code - example programs \ No newline at end of file +Jan 15th, 2003 +v0.11 -- More subtle fixes + -- Moved to gentoo linux [hurrah!] so made *nix specific fixes to the make process + -- Sped up the montgomery reduction code quite a bit + -- fixed up demo so when building timing for the x86 it assumes ELF format now + +Jan 9th, 2003 +v0.10 -- Pekka Riikonen suggested fixes to the radix conversion code. + -- Added baseline montgomery and comba montgomery reductions, sped up exptmods + [to a point, see bn.h for MONTGOMERY_EXPT_CUTOFF] + +Jan 6th, 2003 +v0.09 -- Updated the manual to reflect recent changes. :-) + -- Added Jacobi function (mp_jacobi) to supplement the number theory side of the lib + -- Added a Mersenne prime finder demo in ./etc/mersenne.c + +Jan 2nd, 2003 +v0.08 -- Sped up the multipliers by moving the inner loop variables into a smaller scope + -- Corrected a bunch of small "warnings" + -- Added more comments + -- Made "mtest" be able to use /dev/random, /dev/urandom or stdin for RNG data + -- Corrected some bugs where error messages were potentially ignored + -- add etc/pprime.c program which makes numbers which are provably prime. + +Jan 1st, 2003 +v0.07 -- Removed alot of heap operations from core functions to speed them up + -- Added a root finding function [and mp_sqrt macro like from MPI] + -- Added more to manual + +Dec 31st, 2002 +v0.06 -- Sped up the s_mp_add, s_mp_sub which inturn sped up mp_invmod, mp_exptmod, etc... + -- Cleaned up the header a bit more + +Dec 30th, 2002 +v0.05 -- Builds with MSVC out of the box + -- Fixed a bug in mp_invmod w.r.t. even moduli + -- Made mp_toradix and mp_read_radix use char instead of unsigned char arrays + -- Fixed up exptmod to use fewer multiplications + -- Fixed up mp_init_size to use only one heap operation + -- Note there is a slight "off-by-one" bug in the library somewhere + without the padding (see the source for comment) the library + crashes in libtomcrypt. Anyways a reasonable workaround is to pad the + numbers which will always correct it since as the numbers grow the padding + will still be beyond the end of the number + -- Added more to the manual + +Dec 29th, 2002 +v0.04 -- Fixed a memory leak in mp_to_unsigned_bin + -- optimized invmod code + -- Fixed bug in mp_div + -- use exchange instead of copy for results + -- added a bit more to the manual + +Dec 27th, 2002 +v0.03 -- Sped up s_mp_mul_high_digs by not computing the carries of the lower digits + -- Fixed a bug where mp_set_int wouldn't zero the value first and set the used member. + -- fixed a bug in s_mp_mul_high_digs where the limit placed on the result digits was not calculated properly + -- fixed bugs in add/sub/mul/sqr_mod functions where if the modulus and dest were the same it wouldn't work + -- fixed a bug in mp_mod and mp_mod_d concerning negative inputs + -- mp_mul_d didn't preserve sign + -- Many many many many fixes + -- Works in LibTomCrypt now :-) + -- Added iterations to the timing demos... more accurate. + -- Tom needs a job. + +Dec 26th, 2002 +v0.02 -- Fixed a few "slips" in the manual. This is "LibTomMath" afterall :-) + -- Added mp_cmp_mag, mp_neg, mp_abs and mp_radix_size that were missing. + -- Sped up the fast [comba] multipliers more [yahoo!] + +Dec 25th,2002 +v0.01 -- Initial release. Gimme a break. + -- Todo list, + add details to manual [e.g. algorithms] + more comments in code + example programs diff --git a/demo.c b/demo.c index ab92707..f482120 100644 --- a/demo.c +++ b/demo.c @@ -19,8 +19,10 @@ #ifdef TIMER_X86 #define TIMER -extern ulong64 rdtsc(void); -extern void reset(void); +extern ulong64 _rdtsc(void); +extern void _reset(void); +ulong64 rdtsc(void) { return _rdtsc(); } +void reset(void) { _reset(); } #endif #ifdef TIMER @@ -85,7 +87,6 @@ int main(void) mp_int a, b, c, d, e, f; unsigned long expt_n, add_n, sub_n, mul_n, div_n, sqr_n, mul2d_n, div2d_n, gcd_n, lcm_n, inv_n; int rr; - mp_digit tom; #ifdef TIMER int n; @@ -99,42 +100,33 @@ int main(void) mp_init(&e); mp_init(&f); - mp_read_radix(&a, "59994534535345535344389423", 10); - mp_read_radix(&b, "49993453555234234565675534", 10); - mp_read_radix(&c, "62398923474472948723847281", 10); - - mp_mulmod(&a, &b, &c, &f); - - /* setup mont */ - mp_montgomery_setup(&c, &tom); - mp_mul(&a, &b, &a); - mp_montgomery_reduce(&a, &c, tom); - mp_montgomery_reduce(&a, &c, tom); - mp_lshd(&a, c.used*2); - mp_mod(&a, &c, &a); - - mp_toradix(&a, cmd, 10); - printf("%s\n\n", cmd); - mp_toradix(&f, cmd, 10); - printf("%s\n", cmd); - -/* return 0; */ - - - mp_read_radix(&a, "V//////////////////////////////////////////////////////////////////////////////////////", 64); - mp_reduce_setup(&b, &a); - printf("\n\n----\n\n"); - mp_toradix(&b, buf, 10); - printf("b == %s\n\n\n", buf); - - mp_read_radix(&b, "4982748972349724892742", 10); - mp_sub_d(&a, 1, &c); - mp_exptmod(&b, &c, &a, &d); - mp_toradix(&d, buf, 10); - printf("b^p-1 == %s\n", buf); - +#ifdef DEBUG + mp_read_radix(&a, "347743159439876626079252796797422223177535447388206607607181663903045907591201940478223621722118173270898487582987137708656414344685816179420855160986340457973820182883508387588163122354089264395604796675278966117567294812714812796820596564876450716066283126720010859041484786529056457896367683122960411136319", 10); + mp_read_radix(&b, "347743159439876626079252796797422223177535447388206607607181663903045907591201940478223621722118173270898487582987137708656414344685816179420855160986340457973820182883508387588163122354089264395604796675278966117567294812714812796820596564876450716066283126720010859041484786529056457896367683122960411136318", 10); + mp_set(&c, 1); + reset_timings(); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + mp_exptmod(&c, &b, &a, &d); + dump_timings(); + return 0; +#endif #ifdef TIMER +goto expt; mp_read_radix(&a, "340282366920938463463374607431768211455", 10); mp_read_radix(&b, "340282366920938463463574607431768211455", 10); while (a.used * DIGIT_BIT < 8192) { @@ -182,7 +174,7 @@ int main(void) printf("Multiplying %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)100000)); mp_copy(&b, &a); } - +expt: { char *primes[] = { "17933601194860113372237070562165128350027320072176844226673287945873370751245439587792371960615073855669274087805055507977323024886880985062002853331424203", @@ -206,7 +198,7 @@ int main(void) mp_mod(&b, &c, &b); mp_set(&c, 3); reset(); - for (rr = 0; rr < 35; rr++) { + for (rr = 0; rr < 100; rr++) { mp_exptmod(&c, &b, &a, &d); } tt = rdtsc(); @@ -219,7 +211,7 @@ int main(void) draw(&d); exit(0); } - printf("Exponentiating %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)35)); + printf("Exponentiating %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)100)); } } diff --git a/makefile b/makefile index edaf773..7567d22 100644 --- a/makefile +++ b/makefile @@ -1,13 +1,13 @@ CC = gcc -CFLAGS += -Wall -W -Wshadow -ansi -O3 -fomit-frame-pointer -funroll-loops +CFLAGS += -Wall -W -Wshadow -ansi -O3 -fomit-frame-pointer -funroll-loops -VERSION=0.10 +VERSION=0.11 default: test test: bn.o demo.o $(CC) bn.o demo.o -o demo - cd mtest ; gcc $(CFLAGS) mtest.c -o mtest.exe -s + cd mtest ; gcc $(CFLAGS) mtest.c -o mtest -s # builds the x86 demo test86: @@ -22,9 +22,9 @@ docs: docdvi rm -f bn.log bn.aux bn.dvi clean: - rm -f *.pdf *.o *.exe mtest/*.exe etc/*.exe bn.log bn.aux bn.dvi *.s + rm -f *.pdf *.o *.exe demo mtest/mtest mtest/*.exe etc/*.exe bn.log bn.aux bn.dvi *.log *.s etc/pprime etc/mersenne zipup: clean docs - chdir .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \ + cd .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \ cp -R ./libtommath/* ./libtommath-$(VERSION)/ ; tar -c libtommath-$(VERSION)/* > ltm-$(VERSION).tar ; \ - bzip2 -9vv ltm-$(VERSION).tar ; zip -9 -r ltm-$(VERSION).zip libtommath-$(VERSION)/* \ No newline at end of file + bzip2 -9vv ltm-$(VERSION).tar ; zip -9 -r ltm-$(VERSION).zip libtommath-$(VERSION)/* diff --git a/timer.asm b/timer.asm index 2393250..b317e3e 100644 --- a/timer.asm +++ b/timer.asm @@ -1,34 +1,34 @@ -; Simple RDTSC reader for NASM -; -; build with "nasm -f ___ timer.asm" where ___ is coff or elf [or whatever] -; -; Most *nix installs use elf so it would be "nasm -f elf timer.asm" -; -; Tom St Denis -[bits 32] -[section .data] -timer dd 0, 0 -[section .text] - -[global _gettsc] -_gettsc: - rdtsc - ret - -[global _rdtsc] -_rdtsc: - rdtsc - sub eax,[timer] - sbb edx,[timer+4] - ret - -[global _reset] -_reset: - push eax - push edx - rdtsc - mov [timer],eax - mov [timer+4],edx - pop edx - pop eax - ret \ No newline at end of file +; Simple RDTSC reader for NASM +; +; build with "nasm -f ___ timer.asm" where ___ is coff or elf [or whatever] +; +; Most *nix installs use elf so it would be "nasm -f elf timer.asm" +; +; Tom St Denis +[bits 32] +[section .data] +timer dd 0, 0 +[section .text] + +[global _gettsc] +_gettsc: + rdtsc + ret + +[global _rdtsc] +_rdtsc: + rdtsc + sub eax,[timer] + sbb edx,[timer+4] + ret + +[global _reset] +_reset: + push eax + push edx + rdtsc + mov [timer],eax + mov [timer+4],edx + pop edx + pop eax + ret