From 57354e11ac7611384bc4e6e8dd23eff281ce2a30 Mon Sep 17 00:00:00 2001 From: Tom St Denis Date: Fri, 28 Feb 2003 16:08:34 +0000 Subject: [PATCH] added libtommath-0.12 --- b.bat | 2 - bn.c | 3908 -------------------------------- bn.pdf | Bin 172711 -> 177343 bytes bn.tex | 69 +- bn_fast_mp_invmod.c | 168 ++ bn_fast_mp_montgomery_reduce.c | 116 + bn_fast_s_mp_mul_digs.c | 113 + bn_fast_s_mp_mul_high_digs.c | 86 + bn_fast_s_mp_sqr.c | 112 + bn_mp_2expt.c | 31 + bn_mp_abs.c | 27 + bn_mp_add.c | 56 + bn_mp_add_d.c | 33 + bn_mp_addmod.c | 36 + bn_mp_and.c | 51 + bn_mp_clamp.c | 26 + bn_mp_clear.c | 33 + bn_mp_cmp.c | 30 + bn_mp_cmp_d.c | 37 + bn_mp_cmp_mag.c | 40 + bn_mp_copy.c | 48 + bn_mp_count_bits.c | 35 + bn_mp_div.c | 200 ++ bn_mp_div_2.c | 38 + bn_mp_div_2d.c | 78 + bn_mp_div_d.c | 44 + bn_mp_exch.c | 25 + bn_mp_expt_d.c | 49 + bn_mp_exptmod.c | 213 ++ bn_mp_exptmod_fast.c | 229 ++ bn_mp_gcd.c | 118 + bn_mp_grow.c | 40 + bn_mp_init.c | 35 + bn_mp_init_copy.c | 28 + bn_mp_init_size.c | 33 + bn_mp_invmod.c | 203 ++ bn_mp_jacobi.c | 114 + bn_mp_karatsuba_mul.c | 142 ++ bn_mp_karatsuba_sqr.c | 106 + bn_mp_lcm.c | 42 + bn_mp_lshd.c | 46 + bn_mp_mod.c | 43 + bn_mp_mod_2d.c | 51 + bn_mp_mod_d.c | 47 + bn_mp_montgomery_reduce.c | 80 + bn_mp_montgomery_setup.c | 53 + bn_mp_mul.c | 30 + bn_mp_mul_2.c | 44 + bn_mp_mul_2d.c | 57 + bn_mp_mul_d.c | 46 + bn_mp_mulmod.c | 36 + bn_mp_n_root.c | 115 + bn_mp_neg.c | 27 + bn_mp_or.c | 45 + bn_mp_rand.c | 48 + bn_mp_read_signed_bin.c | 28 + bn_mp_read_unsigned_bin.c | 39 + bn_mp_reduce.c | 95 + bn_mp_rshd.c | 45 + bn_mp_set.c | 24 + bn_mp_set_int.c | 45 + bn_mp_shrink.c | 28 + bn_mp_signed_bin_size.c | 22 + bn_mp_sqr.c | 29 + bn_mp_sqrmod.c | 36 + bn_mp_sub.c | 58 + bn_mp_sub_d.c | 33 + bn_mp_submod.c | 36 + bn_mp_to_signed_bin.c | 28 + bn_mp_to_unsigned_bin.c | 43 + bn_mp_unsigned_bin_size.c | 23 + bn_mp_xor.c | 45 + bn_mp_zero.c | 24 + bn_radix.c | 139 ++ bn_reverse.c | 33 + bn_s_mp_add.c | 91 + bn_s_mp_mul_digs.c | 83 + bn_s_mp_mul_high_digs.c | 77 + bn_s_mp_sqr.c | 89 + bn_s_mp_sub.c | 74 + bncore.c | 19 + changes.txt | 10 + demo.c => demo/demo.c | 48 +- etc/makefile | 16 +- etc/mersenne.c | 228 +- etc/pprime.c | 566 +++-- etc/tune.c | 96 + poly.c => etclib/poly.c | 0 poly.h => etclib/poly.h | 0 ltmtest.exe | Bin 0 -> 49664 bytes makefile | 64 +- mpitest.exe | Bin 0 -> 58368 bytes mtest/mtest.c | 484 ++-- timer.asm | 34 - timings.txt | 39 + bn.h => tommath.h | 55 +- 96 files changed, 5878 insertions(+), 4610 deletions(-) delete mode 100644 b.bat delete mode 100644 bn.c create mode 100644 bn_fast_mp_invmod.c create mode 100644 bn_fast_mp_montgomery_reduce.c create mode 100644 bn_fast_s_mp_mul_digs.c create mode 100644 bn_fast_s_mp_mul_high_digs.c create mode 100644 bn_fast_s_mp_sqr.c create mode 100644 bn_mp_2expt.c create mode 100644 bn_mp_abs.c create mode 100644 bn_mp_add.c create mode 100644 bn_mp_add_d.c create mode 100644 bn_mp_addmod.c create mode 100644 bn_mp_and.c create mode 100644 bn_mp_clamp.c create mode 100644 bn_mp_clear.c create mode 100644 bn_mp_cmp.c create mode 100644 bn_mp_cmp_d.c create mode 100644 bn_mp_cmp_mag.c create mode 100644 bn_mp_copy.c create mode 100644 bn_mp_count_bits.c create mode 100644 bn_mp_div.c create mode 100644 bn_mp_div_2.c create mode 100644 bn_mp_div_2d.c create mode 100644 bn_mp_div_d.c create mode 100644 bn_mp_exch.c create mode 100644 bn_mp_expt_d.c create mode 100644 bn_mp_exptmod.c create mode 100644 bn_mp_exptmod_fast.c create mode 100644 bn_mp_gcd.c create mode 100644 bn_mp_grow.c create mode 100644 bn_mp_init.c create mode 100644 bn_mp_init_copy.c create mode 100644 bn_mp_init_size.c create mode 100644 bn_mp_invmod.c create mode 100644 bn_mp_jacobi.c create mode 100644 bn_mp_karatsuba_mul.c create mode 100644 bn_mp_karatsuba_sqr.c create mode 100644 bn_mp_lcm.c create mode 100644 bn_mp_lshd.c create mode 100644 bn_mp_mod.c create mode 100644 bn_mp_mod_2d.c create mode 100644 bn_mp_mod_d.c create mode 100644 bn_mp_montgomery_reduce.c create mode 100644 bn_mp_montgomery_setup.c create mode 100644 bn_mp_mul.c create mode 100644 bn_mp_mul_2.c create mode 100644 bn_mp_mul_2d.c create mode 100644 bn_mp_mul_d.c create mode 100644 bn_mp_mulmod.c create mode 100644 bn_mp_n_root.c create mode 100644 bn_mp_neg.c create mode 100644 bn_mp_or.c create mode 100644 bn_mp_rand.c create mode 100644 bn_mp_read_signed_bin.c create mode 100644 bn_mp_read_unsigned_bin.c create mode 100644 bn_mp_reduce.c create mode 100644 bn_mp_rshd.c create mode 100644 bn_mp_set.c create mode 100644 bn_mp_set_int.c create mode 100644 bn_mp_shrink.c create mode 100644 bn_mp_signed_bin_size.c create mode 100644 bn_mp_sqr.c create mode 100644 bn_mp_sqrmod.c create mode 100644 bn_mp_sub.c create mode 100644 bn_mp_sub_d.c create mode 100644 bn_mp_submod.c create mode 100644 bn_mp_to_signed_bin.c create mode 100644 bn_mp_to_unsigned_bin.c create mode 100644 bn_mp_unsigned_bin_size.c create mode 100644 bn_mp_xor.c create mode 100644 bn_mp_zero.c create mode 100644 bn_radix.c create mode 100644 bn_reverse.c create mode 100644 bn_s_mp_add.c create mode 100644 bn_s_mp_mul_digs.c create mode 100644 bn_s_mp_mul_high_digs.c create mode 100644 bn_s_mp_sqr.c create mode 100644 bn_s_mp_sub.c create mode 100644 bncore.c rename demo.c => demo/demo.c (94%) create mode 100644 etc/tune.c rename poly.c => etclib/poly.c (100%) rename poly.h => etclib/poly.h (100%) create mode 100644 ltmtest.exe create mode 100644 mpitest.exe delete mode 100644 timer.asm create mode 100644 timings.txt rename bn.h => tommath.h (83%) diff --git a/b.bat b/b.bat deleted file mode 100644 index 606db8a..0000000 --- a/b.bat +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 8175c33..0000000 --- a/bn.c +++ /dev/null @@ -1,3908 +0,0 @@ -/* LibTomMath, multiple-precision integer library -- Tom St Denis - * - * LibTomMath is library that provides for multiple-precision - * integer arithmetic as well as number theoretic functionality. - * - * The library is designed directly after the MPI library by - * Michael Fromberger but has been written from scratch with - * additional optimizations in place. - * - * The library is free for all purposes without any express - * guarantee it works. - * - * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca - */ -#include "bn.h" - -/* chars used in radix conversions */ -static const char *s_rmap = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; - -#undef MIN -#define MIN(x,y) ((x)<(y)?(x):(y)) -#undef MAX -#define MAX(x,y) ((x)>(y)?(x):(y)) - -#ifdef DEBUG - -/* timing data */ -#ifdef TIMER_X86 -extern ulong64 gettsc(void); -#else -ulong64 gettsc(void) { return clock(); } -#endif - -/* structure to hold timing data */ -struct { - char *func; - ulong64 start, end, tot; -} timings[1000000]; - -/* structure to hold consolidated timing data */ -struct _functime { - char *func; - ulong64 tot; -} functime[1000]; - -static char *_funcs[1000]; -int _ifuncs, _itims; - -#define REGFUNC(name) int __IX = _itims++; _funcs[_ifuncs++] = name; timings[__IX].func = name; timings[__IX].start = gettsc(); -#define DECFUNC() timings[__IX].end = gettsc(); --_ifuncs; -#define VERIFY(val) _verify(val, #val, __LINE__); - -/* sort the consolidated timings */ -int qsort_helper(const void *A, const void *B) -{ - struct _functime *a, *b; - - a = (struct _functime *)A; - b = (struct _functime *)B; - - if (a->tot > b->tot) return -1; - if (a->tot < b->tot) return 1; - return 0; -} - -/* reset debugging information */ -void reset_timings(void) -{ - _ifuncs = _itims = 0; -} - -/* dump the timing data */ -void dump_timings(void) -{ - int x, y; - ulong64 total; - - /* first for every find the total time */ - printf("Phase I ... Finding totals (%d samples)...\n", _itims); - for (x = 0; x < _itims; x++) { - timings[x].tot = timings[x].end - timings[x].start; - } - - /* now subtract the time for each function where nested functions occured */ - printf("Phase II ... Finding dependencies...\n"); - for (x = 0; x < _itims-1; x++) { - for (y = x+1; y < _itims && timings[y].start <= timings[x].end; y++) { - timings[x].tot -= timings[y].tot; - if (timings[x].tot > ((ulong64)1 << (ulong64)40)) { - timings[x].tot = 0; - } - } - } - - /* now consolidate all the entries */ - printf("Phase III... Consolidation...\n"); - - memset(&functime, 0, sizeof(functime)); - total = 0; - for (x = 0; x < _itims; x++) { - if (strcmp(timings[x].func, "_verify")) - total += timings[x].tot; - - /* try to find this entry */ - for (y = 0; functime[y].func != NULL; y++) { - if (strcmp(timings[x].func, functime[y].func) == 0) { - break; - } - } - - if (functime[y].func == NULL) { - /* new entry */ - functime[y].func = timings[x].func; - functime[y].tot = timings[x].tot; - } else { - functime[y].tot += timings[x].tot; - } - } - - for (x = 0; functime[x].func != NULL; x++); - - /* sort and dump */ - qsort(&functime, x, sizeof(functime[0]), &qsort_helper); - - for (x = 0; functime[x].func != NULL; x++) { - if (functime[x].tot > 0 && strcmp(functime[x].func, "_verify") != 0) { - printf("%30s: %20llu (%3llu.%03llu %%)\n", functime[x].func, functime[x].tot, (functime[x].tot * (ulong64)100) / total, ((functime[x].tot * (ulong64)100000) / total) % (ulong64)1000); - } - } -} - -static void _verify(mp_int *a, char *name, int line) -{ - int n, y; - static const char *err[] = { "Null DP", "alloc < used", "digits above used" }; - - REGFUNC("_verify"); - - /* dp null ? */ - y = 0; - if (a->dp == NULL) goto error; - - /* used should be <= alloc */ - ++y; - if (a->alloc < a->used) goto error; - - /* digits above used should be zero */ - ++y; - for (n = a->used; n < a->alloc; n++) { - if (a->dp[n]) goto error; - } - - /* ok */ - DECFUNC(); - return; -error: - printf("Error (%s) with variable {%s} on line %d\n", err[y], name, line); - for (n = _ifuncs - 1; n >= 0; n--) { - if (_funcs[n] != NULL) { - printf("> %s\n", _funcs[n]); - } - } - printf("\n"); - exit(0); -} - -#else /* don't use DEBUG stuff so these macros are blank */ - -#define REGFUNC(name) -#define DECFUNC() -#define VERIFY(val) - -#endif - -/* init a new bigint */ -int mp_init(mp_int *a) -{ - REGFUNC("mp_init"); - - /* allocate ram required and clear it */ - a->dp = calloc(sizeof(mp_digit), MP_PREC); - if (a->dp == NULL) { - DECFUNC(); - return MP_MEM; - } - - /* set the used to zero, allocated digit to the default precision - * and sign to positive */ - a->used = 0; - a->alloc = MP_PREC; - a->sign = MP_ZPOS; - - VERIFY(a); - DECFUNC(); - return MP_OKAY; -} - -/* clear one (frees) */ -void mp_clear(mp_int *a) -{ - REGFUNC("mp_clear"); - if (a->dp != NULL) { - VERIFY(a); - - /* first zero the digits */ - memset(a->dp, 0, sizeof(mp_digit) * a->used); - - /* free ram */ - free(a->dp); - - /* reset members to make debugging easier */ - a->dp = NULL; - a->alloc = a->used = 0; - } - DECFUNC(); -} - -void mp_exch(mp_int *a, mp_int *b) -{ - mp_int t; - - REGFUNC("mp_exch"); - VERIFY(a); - VERIFY(b); - t = *a; *a = *b; *b = t; - DECFUNC(); -} - -/* grow as required */ -static int mp_grow(mp_int *a, int size) -{ - int i, n; - - REGFUNC("mp_grow"); - VERIFY(a); - - /* if the alloc size is smaller alloc more ram */ - if (a->alloc < size) { - size += (MP_PREC*2) - (size & (MP_PREC-1)); /* ensure there are always at least 16 digits extra on top */ - - a->dp = realloc(a->dp, sizeof(mp_digit)*size); - if (a->dp == NULL) { - DECFUNC(); - return MP_MEM; - } - - n = a->alloc; - a->alloc = size; - for (i = n; i < a->alloc; i++) { - a->dp[i] = 0; - } - } - DECFUNC(); - return MP_OKAY; -} - -/* shrink a bignum */ -int mp_shrink(mp_int *a) -{ - REGFUNC("mp_shrink"); - VERIFY(a); - if (a->alloc != a->used) { - if ((a->dp = realloc(a->dp, sizeof(mp_digit) * a->used)) == NULL) { - DECFUNC(); - return MP_MEM; - } - a->alloc = a->used; - } - DECFUNC(); - return MP_OKAY; -} - -/* trim unused digits */ -static void mp_clamp(mp_int *a) -{ - REGFUNC("mp_clamp"); - VERIFY(a); - while (a->used > 0 && a->dp[a->used-1] == 0) --(a->used); - if (a->used == 0) { - a->sign = MP_ZPOS; - } - DECFUNC(); -} - -/* set to zero */ -void mp_zero(mp_int *a) -{ - REGFUNC("mp_zero"); - VERIFY(a); - a->sign = MP_ZPOS; - a->used = 0; - memset(a->dp, 0, sizeof(mp_digit) * a->alloc); - DECFUNC(); -} - -/* set to a digit */ -void mp_set(mp_int *a, mp_digit b) -{ - REGFUNC("mp_set"); - VERIFY(a); - mp_zero(a); - a->dp[0] = b & MP_MASK; - a->used = (a->dp[0] != 0) ? 1: 0; - DECFUNC(); -} - -/* set a 32-bit const */ -int mp_set_int(mp_int *a, unsigned long b) -{ - int x, res; - - REGFUNC("mp_set_int"); - VERIFY(a); - mp_zero(a); - - /* set four bits at a time, simplest solution to the what if DIGIT_BIT==7 case */ - for (x = 0; x < 8; x++) { - - /* shift the number up four bits */ - if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { - DECFUNC(); - return res; - } - - /* OR in the top four bits of the source */ - a->dp[0] |= (b>>28)&15; - - /* shift the source up to the next four bits */ - b <<= 4; - - /* ensure that digits are not clamped off */ - a->used += 32/DIGIT_BIT + 1; - } - - mp_clamp(a); - DECFUNC(); - return MP_OKAY; -} - -/* init a mp_init and grow it to a given size */ -int mp_init_size(mp_int *a, int size) -{ - REGFUNC("mp_init_size"); - - /* pad up so there are at least 16 zero digits */ - size += (MP_PREC*2) - (size & (MP_PREC-1)); /* ensure there are always at least 16 digits extra on top */ - a->dp = calloc(sizeof(mp_digit), size); - if (a->dp == NULL) { - DECFUNC(); - return MP_MEM; - } - a->used = 0; - a->alloc = size; - a->sign = MP_ZPOS; - - DECFUNC(); - return MP_OKAY; -} - -/* copy, b = a */ -int mp_copy(mp_int *a, mp_int *b) -{ - int res, n; - - REGFUNC("mp_copy"); - VERIFY(a); - VERIFY(b); - - /* if dst == src do nothing */ - if (a == b || a->dp == b->dp) { - DECFUNC(); - return MP_OKAY; - } - - /* grow dest */ - if ((res = mp_grow(b, a->used)) != MP_OKAY) { - DECFUNC(); - return res; - } - - /* zero b and copy the parameters over */ - b->used = a->used; - b->sign = a->sign; - - /* copy all the digits */ - for (n = 0; n < a->used; n++) { - b->dp[n] = a->dp[n]; - } - - /* clear high digits */ - for (n = b->used; n < b->alloc; n++) { - b->dp[n] = 0; - } - DECFUNC(); - return MP_OKAY; -} - -/* creates "a" then copies b into it */ -int mp_init_copy(mp_int *a, mp_int *b) -{ - int res; - - REGFUNC("mp_init_copy"); - VERIFY(b); - if ((res = mp_init(a)) != MP_OKAY) { - DECFUNC(); - return res; - } - res = mp_copy(b, a); - DECFUNC(); - return res; -} - -/* b = |a| */ -int mp_abs(mp_int *a, mp_int *b) -{ - int res; - REGFUNC("mp_abs"); - VERIFY(a); - VERIFY(b); - if ((res = mp_copy(a, b)) != MP_OKAY) { - DECFUNC(); - return res; - } - b->sign = MP_ZPOS; - DECFUNC(); - return MP_OKAY; -} - -/* b = -a */ -int mp_neg(mp_int *a, mp_int *b) -{ - int res; - REGFUNC("mp_neg"); - VERIFY(a); - VERIFY(b); - if ((res = mp_copy(a, b)) != MP_OKAY) { - DECFUNC(); - return res; - } - b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; - DECFUNC(); - return MP_OKAY; -} - -/* compare maginitude of two ints (unsigned) */ -int mp_cmp_mag(mp_int *a, mp_int *b) -{ - int n; - - REGFUNC("mp_cmp_mag"); - VERIFY(a); - VERIFY(b); - - /* compare based on # of non-zero digits */ - if (a->used > b->used) { - DECFUNC(); - return MP_GT; - } else if (a->used < b->used) { - DECFUNC(); - return MP_LT; - } - - /* compare based on digits */ - for (n = a->used - 1; n >= 0; n--) { - if (a->dp[n] > b->dp[n]) { - DECFUNC(); - return MP_GT; - } else if (a->dp[n] < b->dp[n]) { - DECFUNC(); - return MP_LT; - } - } - DECFUNC(); - return MP_EQ; -} - -/* compare two ints (signed)*/ -int mp_cmp(mp_int *a, mp_int *b) -{ - int res; - REGFUNC("mp_cmp"); - VERIFY(a); - VERIFY(b); - /* compare based on sign */ - if (a->sign == MP_NEG && b->sign == MP_ZPOS) { - DECFUNC(); - return MP_LT; - } else if (a->sign == MP_ZPOS && b->sign == MP_NEG) { - DECFUNC(); - return MP_GT; - } - res = mp_cmp_mag(a, b); - DECFUNC(); - return res; -} - -/* compare a digit */ -int mp_cmp_d(mp_int *a, mp_digit b) -{ - REGFUNC("mp_cmp_d"); - VERIFY(a); - - if (a->sign == MP_NEG) { - DECFUNC(); - return MP_LT; - } - - if (a->used > 1) { - DECFUNC(); - return MP_GT; - } - - if (a->dp[0] > b) { - DECFUNC(); - return MP_GT; - } else if (a->dp[0] < b) { - DECFUNC(); - return MP_LT; - } else { - DECFUNC(); - return MP_EQ; - } -} - -/* shift right a certain amount of digits */ -void mp_rshd(mp_int *a, int b) -{ - int x; - - REGFUNC("mp_rshd"); - VERIFY(a); - - /* if b <= 0 then ignore it */ - if (b <= 0) { - DECFUNC(); - return; - } - - /* if b > used then simply zero it and return */ - if (a->used < b) { - mp_zero(a); - DECFUNC(); - return; - } - - /* shift the digits down */ - for (x = 0; x < (a->used - b); x++) { - a->dp[x] = a->dp[x + b]; - } - - /* zero the top digits */ - for (; x < a->used; x++) { - a->dp[x] = 0; - } - mp_clamp(a); - DECFUNC(); -} - -/* shift left a certain amount of digits */ -int mp_lshd(mp_int *a, int b) -{ - int x, res; - - REGFUNC("mp_lshd"); - VERIFY(a); - - /* if its less than zero return */ - if (b <= 0) { - DECFUNC(); - return MP_OKAY; - } - - /* grow to fit the new digits */ - if ((res = mp_grow(a, a->used + b)) != MP_OKAY) { - DECFUNC(); - return res; - } - - /* increment the used by the shift amount than copy upwards */ - a->used += b; - for (x = a->used-1; x >= b; x--) { - a->dp[x] = a->dp[x - b]; - } - - /* zero the lower digits */ - for (x = 0; x < b; x++) { - a->dp[x] = 0; - } - mp_clamp(a); - DECFUNC(); - return MP_OKAY; -} - -/* calc a value mod 2^b */ -int mp_mod_2d(mp_int *a, int b, mp_int *c) -{ - int x, res; - - REGFUNC("mp_mod_2d"); - VERIFY(a); - VERIFY(c); - - /* if b is <= 0 then zero the int */ - if (b <= 0) { - mp_zero(c); - DECFUNC(); - return MP_OKAY; - } - - /* if the modulus is larger than the value than return */ - if (b > (int)(a->used * DIGIT_BIT)) { - res = mp_copy(a, c); - DECFUNC(); - return res; - } - - /* copy */ - if ((res = mp_copy(a, c)) != MP_OKAY) { - DECFUNC(); - return res; - } - - /* zero digits above the last digit of the modulus */ - for (x = (b/DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { - c->dp[x] = 0; - } - /* clear the digit that is not completely outside/inside the modulus */ - c->dp[b/DIGIT_BIT] &= (mp_digit)((((mp_digit)1)<<(((mp_digit)b) % DIGIT_BIT)) - ((mp_digit)1)); - mp_clamp(c); - DECFUNC(); - return MP_OKAY; -} - -/* shift right by a certain bit count (store quotient in c, remainder in d) */ -int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d) -{ - mp_digit D, r, rr; - int x, res; - mp_int t; - - REGFUNC("mp_div_2d"); - VERIFY(a); - VERIFY(c); - if (d != NULL) { VERIFY(d); } - - /* if the shift count is <= 0 then we do no work */ - if (b <= 0) { - res = mp_copy(a, c); - if (d != NULL) { mp_zero(d); } - DECFUNC(); - return res; - } - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - /* get the remainder */ - if (d != NULL) { - if ((res = mp_mod_2d(a, b, &t)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - } - - /* copy */ - if ((res = mp_copy(a, c)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - - /* shift by as many digits in the bit count */ - mp_rshd(c, b/DIGIT_BIT); - - /* shift any bit count < DIGIT_BIT */ - D = (mp_digit)(b % DIGIT_BIT); - if (D != 0) { - r = 0; - for (x = c->used - 1; x >= 0; x--) { - /* get the lower bits of this word in a temp */ - rr = c->dp[x] & ((mp_digit)((1U<dp[x] = (c->dp[x] >> D) | (r << (DIGIT_BIT-D)); - - /* set the carry to the carry bits of the current word found above */ - r = rr; - } - } - mp_clamp(c); - res = MP_OKAY; - if (d != NULL) { - mp_exch(&t, d); - } - mp_clear(&t); - DECFUNC(); - return MP_OKAY; -} - -/* shift left by a certain bit count */ -int mp_mul_2d(mp_int *a, int b, mp_int *c) -{ - mp_digit d, r, rr; - int x, res; - - REGFUNC("mp_mul_2d"); - VERIFY(a); - VERIFY(c); - - /* copy */ - if ((res = mp_copy(a, c)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_grow(c, c->used + b/DIGIT_BIT + 1)) != MP_OKAY) { - DECFUNC(); - return res; - } - - /* shift by as many digits in the bit count */ - if ((res = mp_lshd(c, b/DIGIT_BIT)) != MP_OKAY) { - DECFUNC(); - return res; - } - c->used = c->alloc; - - /* shift any bit count < DIGIT_BIT */ - d = (mp_digit)(b % DIGIT_BIT); - if (d != 0) { - r = 0; - for (x = 0; x < c->used; x++) { - /* get the higher bits of the current word */ - rr = (c->dp[x] >> (DIGIT_BIT - d)) & ((mp_digit)((1U<dp[x] = ((c->dp[x] << d) | r) & MP_MASK; - - /* set the carry to the carry bits of the current word */ - r = rr; - } - } - mp_clamp(c); - DECFUNC(); - return MP_OKAY; -} - -/* b = a/2 */ -int mp_div_2(mp_int *a, mp_int *b) -{ - mp_digit r, rr; - int x, res; - - REGFUNC("mp_div_2"); - VERIFY(a); - VERIFY(b); - - /* copy */ - if ((res = mp_copy(a, b)) != MP_OKAY) { - DECFUNC(); - return res; - } - - r = 0; - for (x = b->used - 1; x >= 0; x--) { - rr = b->dp[x] & 1; - b->dp[x] = (b->dp[x] >> 1) | (r << (DIGIT_BIT-1)); - r = rr; - } - mp_clamp(b); - DECFUNC(); - return MP_OKAY; -} - -/* b = a*2 */ -int mp_mul_2(mp_int *a, mp_int *b) -{ - mp_digit r, rr; - int x, res; - - REGFUNC("mp_mul_2"); - VERIFY(a); - VERIFY(b); - - /* copy */ - if ((res = mp_copy(a, b)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_grow(b, b->used + 1)) != MP_OKAY) { - DECFUNC(); - return res; - } - ++b->used; - - /* shift any bit count < DIGIT_BIT */ - r = 0; - for (x = 0; x < b->used; x++) { - rr = (b->dp[x] >> (DIGIT_BIT - 1)) & 1; - b->dp[x] = ((b->dp[x] << 1) | r) & MP_MASK; - r = rr; - } - mp_clamp(b); - DECFUNC(); - return MP_OKAY; -} - -/* low level addition, based on HAC pp.594, Algorithm 14.7 */ -static int s_mp_add(mp_int *a, mp_int *b, mp_int *c) -{ - mp_int *x; - int olduse, res, min, max, i; - mp_digit u; - - REGFUNC("s_mp_add"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - /* find sizes, we let |a| <= |b| which means we have to sort - * them. "x" will point to the input with the most digits - */ - if (a->used > b->used) { - min = b->used; - max = a->used; - x = a; - } else if (a->used < b->used) { - min = a->used; - max = b->used; - x = b; - } else { - min = max = a->used; - x = NULL; - } - - /* init result */ - if (c->alloc < max+1) { - if ((res = mp_grow(c, max+1)) != MP_OKAY) { - DECFUNC(); - return res; - } - } - - olduse = c->used; - c->used = max + 1; - - /* add digits from lower part */ - - /* set the carry to zero */ - u = 0; - for (i = 0; i < min; i++) { - /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ - c->dp[i] = a->dp[i] + b->dp[i] + u; - - /* U = carry bit of T[i] */ - u = (c->dp[i] >> DIGIT_BIT) & 1; - - /* take away carry bit from T[i] */ - c->dp[i] &= MP_MASK; - } - - /* now copy higher words if any, that is in A+B if A or B has more digits add those in */ - if (min != max) { - for (; i < max; i++) { - /* T[i] = X[i] + U */ - c->dp[i] = x->dp[i] + u; - - /* U = carry bit of T[i] */ - u = (c->dp[i] >> DIGIT_BIT) & 1; - - /* take away carry bit from T[i] */ - c->dp[i] &= MP_MASK; - } - } - - /* add carry */ - c->dp[i] = u; - - /* clear digits above used (since we may not have grown result above) */ - for (i = c->used; i < olduse; i++) { - c->dp[i] = 0; - } - - mp_clamp(c); - DECFUNC(); - return MP_OKAY; -} - -/* low level subtraction (assumes a > b), HAC pp.595 Algorithm 14.9 */ -static int s_mp_sub(mp_int *a, mp_int *b, mp_int *c) -{ - int olduse, res, min, max, i; - mp_digit u; - - REGFUNC("s_mp_sub"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - /* find sizes */ - min = b->used; - max = a->used; - - /* init result */ - if (c->alloc < max) { - if ((res = mp_grow(c, max)) != MP_OKAY) { - DECFUNC(); - return res; - } - } - olduse = c->used; - c->used = max; - - /* sub digits from lower part */ - - /* set carry to zero */ - u = 0; - for (i = 0; i < min; i++) { - /* T[i] = A[i] - B[i] - U */ - c->dp[i] = a->dp[i] - (b->dp[i] + u); - - /* U = carry bit of T[i] */ - u = (c->dp[i] >> DIGIT_BIT) & 1; - - /* Clear carry from T[i] */ - c->dp[i] &= MP_MASK; - } - - /* now copy higher words if any, e.g. if A has more digits than B */ - if (min != max) { - for (; i < max; i++) { - /* T[i] = A[i] - U */ - c->dp[i] = a->dp[i] - u; - - /* U = carry bit of T[i] */ - u = (c->dp[i] >> DIGIT_BIT) & 1; - - /* Clear carry from T[i] */ - c->dp[i] &= MP_MASK; - } - } - - /* clear digits above used (since we may not have grown result above) */ - for (i = c->used; i < olduse; i++) { - c->dp[i] = 0; - } - - mp_clamp(c); - DECFUNC(); - return MP_OKAY; -} - -/* low level multiplication */ -#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) - -/* Fast (comba) multiplier - * - * This is the fast column-array [comba] multiplier. It is designed to compute - * the columns of the product first then handle the carries afterwards. This - * has the effect of making the nested loops that compute the columns very - * simple and schedulable on super-scalar processors. - * - */ -static int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) -{ - int olduse, res, pa, ix; - mp_word W[512]; - - REGFUNC("fast_s_mp_mul_digs"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - if (c->alloc < digs) { - if ((res = mp_grow(c, digs)) != MP_OKAY) { - DECFUNC(); - return res; - } - } - - /* clear temp buf (the columns) */ - memset(W, 0, sizeof(mp_word) * digs); - - /* calculate the columns */ - pa = a->used; - for (ix = 0; ix < pa; ix++) { - - /* this multiplier has been modified to allow you to control how many digits - * of output are produced. So at most we want to make upto "digs" digits - * of output - */ - - - /* this adds products to distinct columns (at ix+iy) of W - * note that each step through the loop is not dependent on - * the previous which means the compiler can easily unroll - * the loop without scheduling problems - */ - { - register mp_digit tmpx, *tmpy; - register mp_word *_W; - register int iy, pb; - - /* alias for the the word on the left e.g. A[ix] * A[iy] */ - tmpx = a->dp[ix]; - - /* alias for the right side */ - tmpy = b->dp; - - /* alias for the columns, each step through the loop adds a new - term to each column - */ - _W = W + ix; - - /* the number of digits is limited by their placement. E.g. - we avoid multiplying digits that will end up above the # of - digits of precision requested - */ - pb = MIN(b->used, digs - ix); - - for (iy = 0; iy < pb; iy++) { - *_W++ += ((mp_word)tmpx) * ((mp_word)*tmpy++); - } - } - - } - - /* setup dest */ - olduse = c->used; - c->used = digs; - - - /* At this point W[] contains the sums of each column. To get the - * correct result we must take the extra bits from each column and - * carry them down - * - * Note that while this adds extra code to the multiplier it saves time - * since the carry propagation is removed from the above nested loop. - * This has the effect of reducing the work from N*(N+N*c)==N^2 + c*N^2 to - * N^2 + N*c where c is the cost of the shifting. On very small numbers - * this is slower but on most cryptographic size numbers it is faster. - */ - - for (ix = 1; ix < digs; 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[digs-1] = (mp_digit)(W[digs-1] & ((mp_word)MP_MASK)); - - /* clear unused */ - for (; ix < olduse; ix++) { - c->dp[ix] = 0; - } - - mp_clamp(c); - DECFUNC(); - return MP_OKAY; -} - -/* multiplies |a| * |b| and only computes upto digs digits of result - * HAC pp. 595, Algorithm 14.12 Modified so you can control how many digits of - * output are created. - */ -static int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) -{ - mp_int t; - int res, pa, pb, ix, iy; - mp_digit u; - mp_word r; - mp_digit tmpx, *tmpt, *tmpy; - - REGFUNC("s_mp_mul_digs"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - /* can we use the fast multiplier? - * - * The fast multiplier can be used if the output will have less than - * 512 digits and the number of digits won't affect carry propagation - */ - if ((digs < 512) && digs < (1<<( (CHAR_BIT*sizeof(mp_word)) - (2*DIGIT_BIT)))) { - res = fast_s_mp_mul_digs(a,b,c,digs); - DECFUNC(); - return res; - } - - if ((res = mp_init_size(&t, digs)) != MP_OKAY) { - DECFUNC(); - return res; - } - t.used = digs; - - /* compute the digits of the product directly */ - pa = a->used; - for (ix = 0; ix < pa; ix++) { - /* set the carry to zero */ - u = 0; - - /* limit ourselves to making digs digits of output */ - pb = MIN(b->used, digs - ix); - - /* setup some aliases */ - tmpx = a->dp[ix]; - tmpt = &(t.dp[ix]); - tmpy = b->dp; - - /* compute the columns of the output and propagate the carry */ - for (iy = 0; iy < pb; iy++) { - /* compute the column as a mp_word */ - r = ((mp_word)*tmpt) + ((mp_word)tmpx) * ((mp_word)*tmpy++) + ((mp_word)u); - - /* the new column is the lower part of the result */ - *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); - - /* get the carry word from the result */ - u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); - } - if (ix+iyused + b->used + 1; - if (c->alloc < newused) { - if ((res = mp_grow(c, newused)) != MP_OKAY) { - DECFUNC(); - return res; - } - } - - /* like the other comba method we compute the columns first */ - pa = a->used; - pb = b->used; - memset(&W[digs], 0, (pa + pb + 1 - digs) * sizeof(mp_word)); - for (ix = 0; ix < pa; ix++) { - { - register mp_digit tmpx, *tmpy; - register int iy; - register mp_word *_W; - - /* work todo, that is we only calculate digits that are at "digs" or above */ - iy = digs - ix; - - /* copy of word on the left of A[ix] * B[iy] */ - tmpx = a->dp[ix]; - - /* alias for right side */ - tmpy = b->dp + iy; - - /* alias for the columns of output. Offset to be equal to or above the - * smallest digit place requested - */ - _W = &(W[digs]); - - /* compute column products for digits above the minimum */ - for (; iy < pb; iy++) { - *_W++ += ((mp_word)tmpx) * ((mp_word)*tmpy++); - } - } - } - - /* setup dest */ - oldused = c->used; - c->used = newused; - - /* now convert the array W downto what we need */ - 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 < oldused; ix++) { - c->dp[ix] = 0; - } - mp_clamp(c); - DECFUNC(); - return MP_OKAY; -} - -/* multiplies |a| * |b| and does not compute the lower digs digits - * [meant to get the higher part of the product] - */ -static int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) -{ - mp_int t; - int res, pa, pb, ix, iy; - mp_digit u; - mp_word r; - mp_digit tmpx, *tmpt, *tmpy; - - REGFUNC("s_mp_mul_high_digs"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - /* can we use the fast multiplier? */ - if (((a->used + b->used + 1) < 512) && MAX(a->used, b->used) < (1<<( (CHAR_BIT*sizeof(mp_word)) - (2*DIGIT_BIT)))) { - res = fast_s_mp_mul_high_digs(a,b,c,digs); - DECFUNC(); - return res; - } - - if ((res = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) { - DECFUNC(); - return res; - } - t.used = a->used + b->used + 1; - - pa = a->used; - pb = b->used; - for (ix = 0; ix < pa; ix++) { - /* clear the carry */ - u = 0; - - /* left hand side of A[ix] * B[iy] */ - tmpx = a->dp[ix]; - - /* alias to the address of where the digits will be stored */ - tmpt = &(t.dp[digs]); - - /* alias for where to read the right hand side from */ - tmpy = b->dp + (digs - ix); - - for (iy = digs - ix; iy < pb; iy++) { - /* calculate the double precision result */ - r = ((mp_word)*tmpt) + ((mp_word)tmpx) * ((mp_word)*tmpy++) + ((mp_word)u); - - /* get the lower part */ - *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); - - /* carry the carry */ - u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); - } - *tmpt = u; - } - mp_clamp(&t); - mp_exch(&t, c); - mp_clear(&t); - DECFUNC(); - return MP_OKAY; -} - -/* fast squaring - * - * This is the comba method where the columns of the product are computed first - * then the carries are computed. This has the effect of making a very simple - * inner loop that is executed the most - * - * W2 represents the outer products and W the inner. - * - * A further optimizations is made because the inner products are of the form - * "A * B * 2". The *2 part does not need to be computed until the end which is - * good because 64-bit shifts are slow! - * - * - */ -static int fast_s_mp_sqr(mp_int *a, mp_int *b) -{ - int olduse, newused, res, ix, pa; - mp_word W2[512], W[512]; - - REGFUNC("fast_s_mp_sqr"); - VERIFY(a); - VERIFY(b); - - pa = a->used; - newused = pa + pa + 1; - if (b->alloc < newused) { - if ((res = mp_grow(b, newused)) != MP_OKAY) { - DECFUNC(); - return res; - } - } - - /* zero temp buffer (columns) */ - memset(W, 0, (pa+pa+1)*sizeof(mp_word)); - memset(W2, 0, (pa+pa+1)*sizeof(mp_word)); - - for (ix = 0; ix < pa; ix++) { - /* compute the outer product */ - W2[ix+ix] += ((mp_word)a->dp[ix]) * ((mp_word)a->dp[ix]); - - { - register mp_digit tmpx, *tmpy; - register mp_word *_W; - register int iy; - - /* copy of left side */ - tmpx = a->dp[ix]; - - /* alias for right side */ - tmpy = a->dp + (ix + 1); - - _W = &(W[ix+ix+1]); - - /* inner products */ - for (iy = ix + 1; iy < pa; iy++) { - *_W++ += ((mp_word)tmpx) * ((mp_word)*tmpy++); - } - } - } - - /* double first value, since the inner products are half of what they should be */ - W[0] += W[0] + W2[0]; - - /* setup dest */ - olduse = b->used; - b->used = newused; - - /* now compute digits */ - 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[(newused)-1] = (mp_digit)(W[(newused)-1] & ((mp_word)MP_MASK)); - - /* clear high */ - for (; ix < olduse; ix++) { - b->dp[ix] = 0; - } - - /* fix the sign (since we no longer make a fresh temp) */ - b->sign = MP_ZPOS; - - mp_clamp(b); - DECFUNC(); - return MP_OKAY; -} - -/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ -static int s_mp_sqr(mp_int *a, mp_int *b) -{ - mp_int t; - int res, ix, iy, pa; - mp_word r, u; - mp_digit tmpx, *tmpt; - - REGFUNC("s_mp_sqr"); - VERIFY(a); - VERIFY(b); - - /* can we use the fast multiplier? */ - if (((a->used * 2 + 1) < 512) && a->used < (1<<( (CHAR_BIT*sizeof(mp_word)) - (2*DIGIT_BIT) - 1))) { - res = fast_s_mp_sqr(a,b); - DECFUNC(); - return res; - } - - pa = a->used; - if ((res = mp_init_size(&t, pa + pa + 1)) != MP_OKAY) { - DECFUNC(); - return res; - } - t.used = pa + pa + 1; - - for (ix = 0; ix < pa; ix++) { - /* first calculate the digit at 2*ix */ - /* calculate double precision result */ - r = ((mp_word)t.dp[ix+ix]) + ((mp_word)a->dp[ix]) * ((mp_word)a->dp[ix]); - - /* store lower part in result */ - t.dp[ix+ix] = (mp_digit)(r & ((mp_word)MP_MASK)); - - /* get the carry */ - u = (r >> ((mp_word)DIGIT_BIT)); - - /* left hand side of A[ix] * A[iy] */ - tmpx = a->dp[ix]; - - /* alias for where to store the results */ - tmpt = &(t.dp[ix+ix+1]); - for (iy = ix + 1; iy < pa; iy++) { - /* first calculate the product */ - r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); - - /* now calculate the double precision result, note we use - * addition instead of *2 since its easier to optimize - */ - r = ((mp_word)*tmpt) + r + r + ((mp_word)u); - - /* store lower part */ - *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); - - /* get carry */ - u = (r >> ((mp_word)DIGIT_BIT)); - } - r = ((mp_word)*tmpt) + u; - *tmpt = (mp_digit)(r & ((mp_word)MP_MASK)); - u = (r >> ((mp_word)DIGIT_BIT)); - /* propagate upwards */ - ++tmpt; - while (u != ((mp_word)0)) { - r = ((mp_word)*tmpt) + ((mp_word)1); - *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); - u = (r >> ((mp_word)DIGIT_BIT)); - } - } - - mp_clamp(&t); - mp_exch(&t, b); - mp_clear(&t); - DECFUNC(); - return MP_OKAY; -} - -/* high level addition (handles signs) */ -int mp_add(mp_int *a, mp_int *b, mp_int *c) -{ - int sa, sb, res; - - REGFUNC("mp_add"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - sa = a->sign; - sb = b->sign; - - /* handle four cases */ - if (sa == MP_ZPOS && sb == MP_ZPOS) { - /* both positive */ - res = s_mp_add(a, b, c); - c->sign = MP_ZPOS; - } else if (sa == MP_ZPOS && sb == MP_NEG) { - /* a + -b == a - b, but if b>a then we do it as -(b-a) */ - if (mp_cmp_mag(a, b) == MP_LT) { - res = s_mp_sub(b, a, c); - c->sign = MP_NEG; - } else { - res = s_mp_sub(a, b, c); - c->sign = MP_ZPOS; - } - } else if (sa == MP_NEG && sb == MP_ZPOS) { - /* -a + b == b - a, but if a>b then we do it as -(a-b) */ - if (mp_cmp_mag(a, b) == MP_GT) { - res = s_mp_sub(a, b, c); - c->sign = MP_NEG; - } else { - res = s_mp_sub(b, a, c); - c->sign = MP_ZPOS; - } - } else { - /* -a + -b == -(a + b) */ - res = s_mp_add(a, b, c); - c->sign = MP_NEG; - } - DECFUNC(); - return res; -} - -/* high level subtraction (handles signs) */ -int mp_sub(mp_int *a, mp_int *b, mp_int *c) -{ - int sa, sb, res; - - REGFUNC("mp_sub"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - sa = a->sign; - sb = b->sign; - - /* handle four cases */ - if (sa == MP_ZPOS && sb == MP_ZPOS) { - /* both positive, a - b, but if b>a then we do -(b - a) */ - if (mp_cmp_mag(a, b) == MP_LT) { - /* b>a */ - res = s_mp_sub(b, a, c); - c->sign = MP_NEG; - } else { - res = s_mp_sub(a, b, c); - c->sign = MP_ZPOS; - } - } else if (sa == MP_ZPOS && sb == MP_NEG) { - /* a - -b == a + b */ - res = s_mp_add(a, b, c); - c->sign = MP_ZPOS; - } else if (sa == MP_NEG && sb == MP_ZPOS) { - /* -a - b == -(a + b) */ - res = s_mp_add(a, b, c); - c->sign = MP_NEG; - } else { - /* -a - -b == b - a, but if a>b == -(a - b) */ - if (mp_cmp_mag(a, b) == MP_GT) { - res = s_mp_sub(a, b, c); - c->sign = MP_NEG; - } else { - res = s_mp_sub(b, a, c); - c->sign = MP_ZPOS; - } - } - - DECFUNC(); - return res; -} - -/* c = |a| * |b| using Karatsuba Multiplication */ -static int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c) -{ - mp_int x0, x1, y0, y1, t1, t2, x0y0, x1y1; - int B, err, x; - - REGFUNC("mp_karatsuba_mul"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - err = MP_MEM; - - /* min # of digits */ - B = MIN(a->used, b->used); - - /* now divide in two */ - B = B/2; - - /* init copy all the temps */ - if (mp_init_size(&x0, B) != MP_OKAY) goto ERR; - if (mp_init_size(&x1, a->used - B) != MP_OKAY) goto X0; - if (mp_init_size(&y0, B) != MP_OKAY) goto X1; - if (mp_init_size(&y1, b->used - B) != MP_OKAY) goto Y0; - - /* init temps */ - if (mp_init(&t1) != MP_OKAY) goto Y1; - if (mp_init(&t2) != MP_OKAY) goto T1; - if (mp_init(&x0y0) != MP_OKAY) goto T2; - if (mp_init(&x1y1) != MP_OKAY) goto X0Y0; - - /* now shift the digits */ - x0.sign = x1.sign = a->sign; - y0.sign = y1.sign = b->sign; - - x0.used = y0.used = B; - x1.used = a->used - B; - y1.used = b->used - B; - - for (x = 0; x < B; x++) { - x0.dp[x] = a->dp[x]; - y0.dp[x] = b->dp[x]; - } - for (x = B; x < a->used; x++) { - x1.dp[x-B] = a->dp[x]; - } - for (x = B; x < b->used; x++) { - y1.dp[x-B] = b->dp[x]; - } - - mp_clamp(&x0); - mp_clamp(&y0); - - /* now calc the products x0y0 and x1y1 */ - if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY) goto X1Y1; /* x0y0 = x0*y0 */ - if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY) goto X1Y1; /* x1y1 = x1*y1 */ - - /* now calc x1-x0 and y1-y0 */ - if (mp_sub(&x1, &x0, &t1) != MP_OKAY) goto X1Y1; /* t1 = x1 - x0 */ - if (mp_sub(&y1, &y0, &t2) != MP_OKAY) goto X1Y1; /* t2 = y1 - y0 */ - if (mp_mul(&t1, &t2, &t1) != MP_OKAY) goto X1Y1; /* t1 = (x1 - x0) * (y1 - y0) */ - - /* add x0y0 */ - if (mp_add(&x0y0, &x1y1, &t2) != MP_OKAY) goto X1Y1; /* t2 = x0y0 + x1y1 */ - if (mp_sub(&t2, &t1, &t1) != MP_OKAY) goto X1Y1; /* t1 = x0y0 + x1y1 - (x1-x0)*(y1-y0) */ - - /* shift by B */ - if (mp_lshd(&t1, B) != MP_OKAY) goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<sign == b->sign) ? MP_ZPOS : MP_NEG; - if (MIN(a->used, b->used) > KARATSUBA_MUL_CUTOFF) { - res = mp_karatsuba_mul(a, b, c); - } else { - res = s_mp_mul(a, b, c); - } - c->sign = neg; - DECFUNC(); - return res; -} - -/* Karatsuba squaring, computes b = a*a */ -static int mp_karatsuba_sqr(mp_int *a, mp_int *b) -{ - mp_int x0, x1, t1, t2, x0x0, x1x1; - int B, err, x; - - REGFUNC("mp_karatsuba_sqr"); - VERIFY(a); - VERIFY(b); - - err = MP_MEM; - - /* min # of digits */ - B = a->used; - - /* now divide in two */ - B = B/2; - - /* init copy all the temps */ - if (mp_init_size(&x0, B) != MP_OKAY) goto ERR; - if (mp_init_size(&x1, a->used - B) != MP_OKAY) goto X0; - - /* init temps */ - if (mp_init(&t1) != MP_OKAY) goto X1; - if (mp_init(&t2) != MP_OKAY) goto T1; - if (mp_init(&x0x0) != MP_OKAY) goto T2; - if (mp_init(&x1x1) != MP_OKAY) goto X0X0; - - /* now shift the digits */ - for (x = 0; x < B; x++) { - x0.dp[x] = a->dp[x]; - } - - for (x = B; x < a->used; x++) { - x1.dp[x-B] = a->dp[x]; - } - - x0.used = B; - x1.used = a->used - B; - - mp_clamp(&x0); - - /* 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 */ - - /* 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) */ - - /* add x0y0 */ - if (mp_add(&x0x0, &x1x1, &t2) != MP_OKAY) goto X1X1; /* t2 = x0y0 + x1y1 */ - if (mp_sub(&t2, &t1, &t1) != MP_OKAY) goto X1X1; /* t1 = x0y0 + x1y1 - (x1-x0)*(y1-y0) */ - - /* shift by B */ - if (mp_lshd(&t1, B) != MP_OKAY) goto X1X1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used > KARATSUBA_SQR_CUTOFF) { - res = mp_karatsuba_sqr(a, b); - } else { - res = s_mp_sqr(a, b); - } - b->sign = MP_ZPOS; - DECFUNC(); - return res; -} - - -/* integer signed division. c*b + d == a [e.g. a/b, c=quotient, d=remainder] - * HAC pp.598 Algorithm 14.20 - * - * Note that the description in HAC is horribly incomplete. For example, - * it doesn't consider the case where digits are removed from 'x' in the inner - * loop. It also doesn't consider the case that y has fewer than three digits, etc.. - * - * The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. -*/ -int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) -{ - mp_int q, x, y, t1, t2; - int res, n, t, i, norm, neg; - - REGFUNC("mp_div"); - VERIFY(a); - VERIFY(b); - if (c != NULL) { VERIFY(c); } - if (d != NULL) { VERIFY(d); } - - /* is divisor zero ? */ - if (mp_iszero(b) == 1) { - DECFUNC(); - return MP_VAL; - } - - /* if a < b then q=0, r = a */ - if (mp_cmp_mag(a, b) == MP_LT) { - if (d != NULL) { - res = mp_copy(a, d); - } else { - res = MP_OKAY; - } - if (c != NULL) { - mp_zero(c); - } - DECFUNC(); - return res; - } - - if ((res = mp_init_size(&q, a->used + 2)) != MP_OKAY) { - DECFUNC(); - return res; - } - q.used = a->used + 2; - - if ((res = mp_init(&t1)) != MP_OKAY) { - goto __Q; - } - - if ((res = mp_init(&t2)) != MP_OKAY) { - goto __T1; - } - - if ((res = mp_init_copy(&x, a)) != MP_OKAY) { - goto __T2; - } - - if ((res = mp_init_copy(&y, b)) != MP_OKAY) { - goto __X; - } - - /* fix the sign */ - neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; - x.sign = y.sign = MP_ZPOS; - - /* normalize both x and y, ensure that y >= b/2, [b == 2^DIGIT_BIT] */ - norm = 0; - while ((y.dp[y.used-1] & (((mp_digit)1)<<(DIGIT_BIT-1))) == ((mp_digit)0)) { - ++norm; - if ((res = mp_mul_2d(&x, 1, &x)) != MP_OKAY) { - goto __Y; - } - if ((res = mp_mul_2d(&y, 1, &y)) != MP_OKAY) { - goto __Y; - } - } - - /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ - n = x.used - 1; - t = y.used - 1; - - /* step 2. while (x >= y*b^n-t) do { q[n-t] += 1; x -= y*b^{n-t} } */ - if ((res = mp_lshd(&y, n - t)) != MP_OKAY) { /* y = y*b^{n-t} */ - goto __Y; - } - - while (mp_cmp(&x, &y) != MP_LT) { - ++(q.dp[n - t]); - if ((res = mp_sub(&x, &y, &x)) != MP_OKAY) { - goto __Y; - } - } - - /* reset y by shifting it back down */ - mp_rshd(&y, n - t); - - /* step 3. for i from n down to (t + 1) */ - for (i = n; i >= (t + 1); i--) { - if (i > x.alloc) continue; - - /* step 3.1 if xi == yt then set q{i-t-1} to b-1, otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ - if (x.dp[i] == y.dp[t]) { - q.dp[i - t - 1] = ((1UL< (mp_word)MP_MASK) tmp = MP_MASK; - q.dp[i - t - 1] = (mp_digit)(tmp & (mp_word)(MP_MASK)); - } - - /* step 3.2 while (q{i-t-1} * (yt * b + y{t-1})) > xi * b^2 + xi-1 * b + xi-2 do q{i-t-1} -= 1; */ - q.dp[i-t-1] = (q.dp[i-t-1] + 1) & MP_MASK; - do { - q.dp[i-t-1] = (q.dp[i-t-1] - 1) & MP_MASK; - - /* find left hand */ - mp_zero(&t1); - t1.dp[0] = (t-1 < 0) ? 0 : y.dp[t-1]; - t1.dp[1] = y.dp[t]; - t1.used = 2; - if ((res = mp_mul_d(&t1, q.dp[i-t-1], &t1)) != MP_OKAY) { - goto __Y; - } - - /* find right hand */ - t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i-2]; - t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i-1]; - t2.dp[2] = x.dp[i]; - t2.used = 3; - } while (mp_cmp(&t1, &t2) == MP_GT); - - /* step 3.3 x = x - q{i-t-1} * y * b^{i-t-1} */ - if ((res = mp_mul_d(&y, q.dp[i-t-1], &t1)) != MP_OKAY) { - goto __Y; - } - - if ((res = mp_lshd(&t1, i - t - 1)) != MP_OKAY) { - goto __Y; - } - - if ((res = mp_sub(&x, &t1, &x)) != MP_OKAY) { - goto __Y; - } - - /* step 3.4 if x < 0 then { x = x + y*b^{i-t-1}; q{i-t-1} -= 1; } */ - if (x.sign == MP_NEG) { - if ((res = mp_copy(&y, &t1)) != MP_OKAY) { - goto __Y; - } - if ((res = mp_lshd(&t1, i-t-1)) != MP_OKAY) { - goto __Y; - } - if ((res = mp_add(&x, &t1, &x)) != MP_OKAY) { - goto __Y; - } - - q.dp[i-t-1] = (q.dp[i-t-1] - 1UL) & MP_MASK; - } - } - - /* now q is the quotient and x is the remainder [which we have to normalize] */ - /* get sign before writing to c */ - x.sign = a->sign; - if (c != NULL) { - mp_clamp(&q); - mp_exch(&q, c); - c->sign = neg; - } - - if (d != NULL) { - mp_div_2d(&x, norm, &x, NULL); - mp_clamp(&x); - mp_exch(&x, d); - } - - res = MP_OKAY; - -__Y: mp_clear(&y); -__X: mp_clear(&x); -__T2: mp_clear(&t2); -__T1: mp_clear(&t1); -__Q: mp_clear(&q); - DECFUNC(); - return res; -} - -/* c = a mod b, 0 <= c < b */ -int mp_mod(mp_int *a, mp_int *b, mp_int *c) -{ - mp_int t; - int res; - - REGFUNC("mp_mod"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_div(a, b, NULL, &t)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - - if (t.sign == MP_NEG) { - res = mp_add(b, &t, c); - } else { - res = MP_OKAY; - mp_exch(&t, c); - } - - mp_clear(&t); - DECFUNC(); - return res; -} - -/* single digit addition */ -int mp_add_d(mp_int *a, mp_digit b, mp_int *c) -{ - mp_int t; - int res; - - REGFUNC("mp_add_d"); - VERIFY(a); - VERIFY(c); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - mp_set(&t, b); - res = mp_add(a, &t, c); - - mp_clear(&t); - DECFUNC(); - return res; -} - -/* single digit subtraction */ -int mp_sub_d(mp_int *a, mp_digit b, mp_int *c) -{ - mp_int t; - int res; - - REGFUNC("mp_sub_d"); - VERIFY(a); - VERIFY(c); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - mp_set(&t, b); - res = mp_sub(a, &t, c); - - mp_clear(&t); - DECFUNC(); - return res; -} - -/* multiply by a digit */ -int mp_mul_d(mp_int *a, mp_digit b, mp_int *c) -{ - int res, pa, ix; - mp_word r; - mp_digit u; - mp_int t; - - REGFUNC("mp_mul_d"); - VERIFY(a); - VERIFY(c); - - pa = a->used; - if ((res = mp_init_size(&t, pa + 2)) != MP_OKAY) { - DECFUNC(); - return res; - } - t.used = pa + 2; - - u = 0; - for (ix = 0; ix < pa; ix++) { - r = ((mp_word)u) + ((mp_word)a->dp[ix]) * ((mp_word)b); - t.dp[ix] = (mp_digit)(r & ((mp_word)MP_MASK)); - u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); - } - t.dp[ix] = u; - - t.sign = a->sign; - mp_clamp(&t); - mp_exch(&t, c); - mp_clear(&t); - DECFUNC(); - return MP_OKAY; -} - -/* single digit division */ -int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d) -{ - mp_int t, t2; - int res; - - REGFUNC("mp_div_d"); - VERIFY(a); - if (c != NULL) { VERIFY(c); } - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_init(&t2)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - - mp_set(&t, b); - res = mp_div(a, &t, c, &t2); - - if (d != NULL) { - *d = t2.dp[0]; - } - - mp_clear(&t); - mp_clear(&t2); - DECFUNC(); - return res; -} - -int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c) -{ - mp_int t, t2; - int res; - - REGFUNC("mp_mod_d"); - VERIFY(a); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_init(&t2)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - - mp_set(&t, b); - mp_div(a, &t, NULL, &t2); - - if (t2.sign == MP_NEG) { - if ((res = mp_add_d(&t2, b, &t2)) != MP_OKAY) { - mp_clear(&t); - mp_clear(&t2); - DECFUNC(); - return res; - } - } - *c = t2.dp[0]; - mp_clear(&t); - mp_clear(&t2); - DECFUNC(); - return MP_OKAY; -} - -int mp_expt_d(mp_int *a, mp_digit b, mp_int *c) -{ - int res, x; - mp_int g; - - REGFUNC("mp_expt_d"); - VERIFY(a); - VERIFY(c); - - if ((res = mp_init_copy(&g, a)) != MP_OKAY) { - DECFUNC(); - return res; - } - - /* set initial result */ - mp_set(c, 1); - - for (x = 0; x < (int)DIGIT_BIT; x++) { - if ((res = mp_sqr(c, c)) != MP_OKAY) { - mp_clear(&g); - DECFUNC(); - return res; - } - - if ((b & (mp_digit)(1<<(DIGIT_BIT-1))) != 0) { - if ((res = mp_mul(c, &g, c)) != MP_OKAY) { - mp_clear(&g); - DECFUNC(); - return res; - } - } - - b <<= 1; - } - - mp_clear(&g); - DECFUNC(); - return MP_OKAY; -} - -/* simple modular functions */ - -/* d = a + b (mod c) */ -int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) -{ - int res; - mp_int t; - - REGFUNC("mp_addmod"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - VERIFY(d); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_add(a, b, &t)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - res = mp_mod(&t, c, d); - mp_clear(&t); - DECFUNC(); - return res; -} - -/* d = a - b (mod c) */ -int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) -{ - int res; - mp_int t; - - REGFUNC("mp_submod"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - VERIFY(d); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_sub(a, b, &t)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - res = mp_mod(&t, c, d); - mp_clear(&t); - DECFUNC(); - return res; -} - -/* d = a * b (mod c) */ -int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) -{ - int res; - mp_int t; - - REGFUNC("mp_mulmod"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - VERIFY(d); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_mul(a, b, &t)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - res = mp_mod(&t, c, d); - mp_clear(&t); - DECFUNC(); - return res; -} - -/* c = a * a (mod b) */ -int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c) -{ - int res; - mp_int t; - - REGFUNC("mp_sqrmod"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_sqr(a, &t)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - res = mp_mod(&t, b, c); - mp_clear(&t); - DECFUNC(); - return res; -} - -/* Greatest Common Divisor using the binary method [Algorithm B, page 338, vol2 of TAOCP] - */ -int mp_gcd(mp_int *a, mp_int *b, mp_int *c) -{ - mp_int u, v, t; - int k, res, neg; - - REGFUNC("mp_gcd"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - /* either zero than gcd is the largest */ - if (mp_iszero(a) == 1 && mp_iszero(b) == 0) { - DECFUNC(); - return mp_copy(b, c); - } - if (mp_iszero(a) == 0 && mp_iszero(b) == 1) { - DECFUNC(); - return mp_copy(a, c); - } - if (mp_iszero(a) == 1 && mp_iszero(b) == 1) { - mp_set(c, 1); - DECFUNC(); - return MP_OKAY; - } - - /* if both are negative they share (-1) as a common divisor */ - neg = (a->sign == b->sign) ? a->sign : MP_ZPOS; - - if ((res = mp_init_copy(&u, a)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_init_copy(&v, b)) != MP_OKAY) { - goto __U; - } - - /* must be positive for the remainder of the algorithm */ - u.sign = v.sign = MP_ZPOS; - - if ((res = mp_init(&t)) != MP_OKAY) { - goto __V; - } - - /* B1. Find power of two */ - k = 0; - while ((u.dp[0] & 1) == 0 && (v.dp[0] & 1) == 0) { - ++k; - if ((res = mp_div_2d(&u, 1, &u, NULL)) != MP_OKAY) { - goto __T; - } - if ((res = mp_div_2d(&v, 1, &v, NULL)) != MP_OKAY) { - goto __T; - } - } - - /* B2. Initialize */ - if ((u.dp[0] & 1) == 1) { - if ((res = mp_copy(&v, &t)) != MP_OKAY) { - goto __T; - } - t.sign = MP_NEG; - } else { - if ((res = mp_copy(&u, &t)) != MP_OKAY) { - goto __T; - } - } - - do { - /* B3 (and B4). Halve t, if even */ - while (t.used != 0 && (t.dp[0] & 1) == 0) { - if ((res = mp_div_2d(&t, 1, &t, NULL)) != MP_OKAY) { - goto __T; - } - } - - /* B5. if t>0 then u=t otherwise v=-t */ - if (t.used != 0 && t.sign != MP_NEG) { - if ((res = mp_copy(&t, &u)) != MP_OKAY) { - goto __T; - } - } else { - if ((res = mp_copy(&t, &v)) != MP_OKAY) { - goto __T; - } - v.sign = (v.sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; - } - - /* B6. t = u - v, if t != 0 loop otherwise terminate */ - if ((res = mp_sub(&u, &v, &t)) != MP_OKAY) { - goto __T; - } - } while (t.used != 0); - - if ((res = mp_mul_2d(&u, k, &u)) != MP_OKAY) { - goto __T; - } - - mp_exch(&u, c); - c->sign = neg; - res = MP_OKAY; -__T: mp_clear(&t); -__V: mp_clear(&u); -__U: mp_clear(&v); - DECFUNC(); - return res; -} - -/* computes least common multipble as a*b/(a, b) */ -int mp_lcm(mp_int *a, mp_int *b, mp_int *c) -{ - int res; - mp_int t; - - REGFUNC("mp_lcm"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - if ((res = mp_init(&t)) != MP_OKAY) { - DECFUNC(); - return res; - } - - if ((res = mp_mul(a, b, &t)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - - if ((res = mp_gcd(a, b, c)) != MP_OKAY) { - mp_clear(&t); - DECFUNC(); - return res; - } - - res = mp_div(&t, c, c, NULL); - mp_clear(&t); - DECFUNC(); - return res; -} - -/* computes the modular inverse via binary extended euclidean algorithm, that is c = 1/a mod b */ -static int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c) -{ - mp_int x, y, u, v, B, D; - int res, neg; - - REGFUNC("fast_mp_invmod"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - if ((res = mp_init(&x)) != MP_OKAY) { - goto __ERR; - } - - if ((res = mp_init(&y)) != MP_OKAY) { - goto __X; - } - - if ((res = mp_init(&u)) != MP_OKAY) { - goto __Y; - } - - if ((res = mp_init(&v)) != MP_OKAY) { - goto __U; - } - - if ((res = mp_init(&B)) != MP_OKAY) { - goto __V; - } - - if ((res = mp_init(&D)) != MP_OKAY) { - goto __B; - } - - /* x == modulus, y == value to invert */ - if ((res = mp_copy(b, &x)) != MP_OKAY) { - goto __D; - } - if ((res = mp_copy(a, &y)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_abs(&y, &y)) != MP_OKAY) { - goto __D; - } - - /* 2. [modified] if x,y are both even then return an error! */ - if (mp_iseven(&x) == 1 && mp_iseven(&y) == 1) { - res = MP_VAL; - goto __D; - } - - /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ - if ((res = mp_copy(&x, &u)) != MP_OKAY) { - goto __D; - } - if ((res = mp_copy(&y, &v)) != MP_OKAY) { - goto __D; - } - mp_set(&D, 1); - - -top: - /* 4. while u is even do */ - while (mp_iseven(&u) == 1) { - /* 4.1 u = u/2 */ - if ((res = mp_div_2(&u, &u)) != MP_OKAY) { - goto __D; - } - /* 4.2 if A or B is odd then */ - if (mp_iseven(&B) == 0) { - if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { - goto __D; - } - } - /* A = A/2, B = B/2 */ - if ((res = mp_div_2(&B, &B)) != MP_OKAY) { - goto __D; - } - } - - - /* 5. while v is even do */ - while (mp_iseven(&v) == 1) { - /* 5.1 v = v/2 */ - if ((res = mp_div_2(&v, &v)) != MP_OKAY) { - goto __D; - } - /* 5.2 if C,D are even then */ - if (mp_iseven(&D) == 0) { - /* C = (C+y)/2, D = (D-x)/2 */ - if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { - goto __D; - } - } - /* C = C/2, D = D/2 */ - if ((res = mp_div_2(&D, &D)) != MP_OKAY) { - goto __D; - } - } - - /* 6. if u >= v then */ - if (mp_cmp(&u, &v) != MP_LT) { - /* u = u - v, A = A - C, B = B - D */ - if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { - goto __D; - } - } else { - /* v - v - u, C = C - A, D = D - B */ - if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { - goto __D; - } - } - - /* if not zero goto step 4 */ - if (mp_iszero(&u) == 0) goto top; - - /* now a = C, b = D, gcd == g*v */ - - /* if v != 1 then there is no inverse */ - if (mp_cmp_d(&v, 1) != MP_EQ) { - res = MP_VAL; - goto __D; - } - - /* b is now the inverse */ - neg = a->sign; - while (D.sign == MP_NEG) { - if ((res = mp_add(&D, b, &D)) != MP_OKAY) { - goto __D; - } - } - mp_exch(&D, c); - c->sign = neg; - res = MP_OKAY; - -__D: mp_clear(&D); -__B: mp_clear(&B); -__V: mp_clear(&v); -__U: mp_clear(&u); -__Y: mp_clear(&y); -__X: mp_clear(&x); -__ERR: - DECFUNC(); - return res; -} - -int mp_invmod(mp_int *a, mp_int *b, mp_int *c) -{ - mp_int x, y, u, v, A, B, C, D; - int res; - - REGFUNC("mp_invmod"); - VERIFY(a); - VERIFY(b); - VERIFY(c); - - /* b cannot be negative */ - if (b->sign == MP_NEG) { - return MP_VAL; - } - - /* if the modulus is odd we can use a faster routine instead */ - if (mp_iseven(b) == 0) { - res = fast_mp_invmod(a,b,c); - DECFUNC(); - return res; - } - - if ((res = mp_init(&x)) != MP_OKAY) { - goto __ERR; - } - - if ((res = mp_init(&y)) != MP_OKAY) { - goto __X; - } - - if ((res = mp_init(&u)) != MP_OKAY) { - goto __Y; - } - - if ((res = mp_init(&v)) != MP_OKAY) { - goto __U; - } - - if ((res = mp_init(&A)) != MP_OKAY) { - goto __V; - } - - if ((res = mp_init(&B)) != MP_OKAY) { - goto __A; - } - - if ((res = mp_init(&C)) != MP_OKAY) { - goto __B; - } - - if ((res = mp_init(&D)) != MP_OKAY) { - goto __C; - } - - /* x = a, y = b */ - if ((res = mp_copy(a, &x)) != MP_OKAY) { - goto __D; - } - if ((res = mp_copy(b, &y)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_abs(&x, &x)) != MP_OKAY) { - goto __D; - } - - /* 2. [modified] if x,y are both even then return an error! */ - if (mp_iseven(&x) == 1 && mp_iseven(&y) == 1) { - res = MP_VAL; - goto __D; - } - - /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ - if ((res = mp_copy(&x, &u)) != MP_OKAY) { - goto __D; - } - if ((res = mp_copy(&y, &v)) != MP_OKAY) { - goto __D; - } - mp_set(&A, 1); - mp_set(&D, 1); - - -top: - /* 4. while u is even do */ - while (mp_iseven(&u) == 1) { - /* 4.1 u = u/2 */ - if ((res = mp_div_2(&u, &u)) != MP_OKAY) { - goto __D; - } - /* 4.2 if A or B is odd then */ - if (mp_iseven(&A) == 0 || mp_iseven(&B) == 0) { - /* A = (A+y)/2, B = (B-x)/2 */ - if ((res = mp_add(&A, &y, &A)) != MP_OKAY) { - goto __D; - } - if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { - goto __D; - } - } - /* A = A/2, B = B/2 */ - if ((res = mp_div_2(&A, &A)) != MP_OKAY) { - goto __D; - } - if ((res = mp_div_2(&B, &B)) != MP_OKAY) { - goto __D; - } - } - - - /* 5. while v is even do */ - while (mp_iseven(&v) == 1) { - /* 5.1 v = v/2 */ - if ((res = mp_div_2(&v, &v)) != MP_OKAY) { - goto __D; - } - /* 5.2 if C,D are even then */ - if (mp_iseven(&C) == 0 || mp_iseven(&D) == 0) { - /* C = (C+y)/2, D = (D-x)/2 */ - if ((res = mp_add(&C, &y, &C)) != MP_OKAY) { - goto __D; - } - if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { - goto __D; - } - } - /* C = C/2, D = D/2 */ - if ((res = mp_div_2(&C, &C)) != MP_OKAY) { - goto __D; - } - if ((res = mp_div_2(&D, &D)) != MP_OKAY) { - goto __D; - } - } - - /* 6. if u >= v then */ - if (mp_cmp(&u, &v) != MP_LT) { - /* u = u - v, A = A - C, B = B - D */ - if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_sub(&A, &C, &A)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { - goto __D; - } - } else { - /* v - v - u, C = C - A, D = D - B */ - if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_sub(&C, &A, &C)) != MP_OKAY) { - goto __D; - } - - if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { - goto __D; - } - } - - /* if not zero goto step 4 */ - if (mp_iszero(&u) == 0) goto top; - - /* now a = C, b = D, gcd == g*v */ - - /* if v != 1 then there is no inverse */ - if (mp_cmp_d(&v, 1) != MP_EQ) { - res = MP_VAL; - goto __D; - } - - /* a is now the inverse */ - mp_exch(&C, c); - res = MP_OKAY; - -__D: mp_clear(&D); -__C: mp_clear(&C); -__B: mp_clear(&B); -__A: mp_clear(&A); -__V: mp_clear(&v); -__U: mp_clear(&u); -__Y: mp_clear(&y); -__X: mp_clear(&x); -__ERR: - DECFUNC(); - return res; -} - -/* pre-calculate the value required for Barrett reduction - * For a given modulus "b" it calulates the value required in "a" - */ -int mp_reduce_setup(mp_int *a, mp_int *b) -{ - int res; - - REGFUNC("mp_reduce_setup"); - VERIFY(a); - VERIFY(b); - - if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { - DECFUNC(); - return res; - } - res = mp_div(a, b, a, NULL); - DECFUNC(); - return res; -} - -/* reduces x mod m, assumes 0 < x < m^2, mu is precomputed via mp_reduce_setup - * From HAC pp.604 Algorithm 14.42 - */ -int mp_reduce(mp_int *x, mp_int *m, mp_int *mu) -{ - mp_int q; - int res, um = m->used; - - REGFUNC("mp_reduce"); - VERIFY(x); - VERIFY(m); - VERIFY(mu); - - if((res = mp_init_copy(&q, x)) != MP_OKAY) { - DECFUNC(); - return res; - } - - mp_rshd(&q, um - 1); /* q1 = x / b^(k-1) */ - - /* according to HAC this is optimization is ok */ - if (((unsigned long)m->used) > (1UL<<(unsigned long)(DIGIT_BIT-1UL))) { - if ((res = mp_mul(&q, mu, &q)) != MP_OKAY) { - goto CLEANUP; - } - } else { - if ((res = s_mp_mul_high_digs(&q, mu, &q, um-1)) != MP_OKAY) { - goto CLEANUP; - } - } - - mp_rshd(&q, um + 1); /* q3 = q2 / b^(k+1) */ - - /* x = x mod b^(k+1), quick (no division) */ - if ((res = mp_mod_2d(x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { - goto CLEANUP; - } - - /* q = q * m mod b^(k+1), quick (no division) */ - if ((res = s_mp_mul_digs(&q, m, &q, um + 1)) != MP_OKAY) { - goto CLEANUP; - } - - /* x = x - q */ - if((res = mp_sub(x, &q, x)) != MP_OKAY) - goto CLEANUP; - - /* If x < 0, add b^(k+1) to it */ - if(mp_cmp_d(x, 0) == MP_LT) { - mp_set(&q, 1); - if((res = mp_lshd(&q, um + 1)) != MP_OKAY) - goto CLEANUP; - if((res = mp_add(x, &q, x)) != MP_OKAY) - goto CLEANUP; - } - - /* Back off if it's too big */ - while(mp_cmp(x, m) != MP_LT) { - if((res = s_mp_sub(x, m, x)) != MP_OKAY) - break; - } - - CLEANUP: - mp_clear(&q); - DECFUNC(); - - return res; -} - -/* setups the montgomery reduction stuff */ -int mp_montgomery_setup(mp_int *a, mp_digit *mp) -{ - mp_int t, tt; - int res; - - if ((res = mp_init(&t)) != MP_OKAY) { - return res; - } - - if ((res = mp_init(&tt)) != MP_OKAY) { - goto __T; - } - - /* tt = b */ - tt.dp[0] = 0; - tt.dp[1] = 1; - tt.used = 2; - - /* t = m mod b */ - t.dp[0] = a->dp[0]; - t.used = 1; - - /* t = 1/m mod b */ - if ((res = mp_invmod(&t, &tt, &t)) != MP_OKAY) { - goto __TT; - } - - /* t = -1/m mod b */ - *mp = ((mp_digit)1 << ((mp_digit)DIGIT_BIT)) - t.dp[0]; - - res = MP_OKAY; -__TT: mp_clear(&tt); -__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) -{ - int ix, res, olduse; - 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+1) { - if ((res = mp_grow(a, m->used+1)) != MP_OKAY) { - DECFUNC(); - return res; - } - } - - /* 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 - * - * 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 - * - * 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; - register mp_word *_W; - - /* aliases */ - 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 next digit, W[ix+1] */ - W[ix+1] += W[ix] >> ((mp_word)DIGIT_BIT); - } - - /* nox fix rest of carries */ - for (++ix; ix <= m->used * 2 + 1; ix++) { - W[ix] += (W[ix-1] >> ((mp_word)DIGIT_BIT)); - } - - /* 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] & ((mp_word)MP_MASK); - } - - /* set the max used */ - a->used = m->used + 1; - - /* 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; - } - - mp_clamp(a); - - /* 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; -} - -/* computes xR^-1 == x (mod N) via Montgomery Reduction */ -int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp) -{ - int ix, res, digs; - mp_digit ui; - - REGFUNC("mp_montgomery_reduce"); - VERIFY(a); - VERIFY(m); - - digs = m->used * 2 + 1; - if ((digs < 512) && digs < (1<<( (CHAR_BIT*sizeof(mp_word)) - (2*DIGIT_BIT)))) { - res = fast_mp_montgomery_reduce(a, m, mp); - DECFUNC(); - return res; - } - - if (a->alloc < m->used*2+1) { - if ((res = mp_grow(a, m->used*2+1)) != MP_OKAY) { - DECFUNC(); - return res; - } - } - a->used = m->used * 2 + 1; - - for (ix = 0; ix < m->used; ix++) { - /* ui = ai * m' mod b */ - ui = (a->dp[ix] * mp) & MP_MASK; - - /* a = a + ui * m * b^i */ - { - register int iy; - register mp_digit *tmpx, *tmpy, mu; - register mp_word r; - - /* aliases */ - tmpx = m->dp; - tmpy = a->dp + ix; - - mu = 0; - for (iy = 0; iy < m->used; iy++) { - r = ((mp_word)ui) * ((mp_word)*tmpx++) + ((mp_word)mu) + ((mp_word)*tmpy); - mu = (r >> ((mp_word)DIGIT_BIT)); - *tmpy++ = (r & ((mp_word)MP_MASK)); - } - /* propagate carries */ - while (mu) { - *tmpy += mu; - mu = (*tmpy>>DIGIT_BIT)&1; - *tmpy++ &= MP_MASK; - } - } - } - - /* A = A/b^n */ - mp_rshd(a, m->used); - - /* 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; -} - -/* computes Y == G^X mod P, HAC pp.616, Algorithm 14.85 - * - * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. - * The value of k changes based on the size of the exponent. - * - * Uses Montgomery reduction - */ -static int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) -{ - mp_int M[256], res; - mp_digit buf, mp; - int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; - - REGFUNC("mp_exptmod_fast"); - VERIFY(G); - VERIFY(X); - VERIFY(P); - VERIFY(Y); - - /* find window size */ - x = mp_count_bits(X); - 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 * DIGIT_BIT)) != MP_OKAY) { - goto __RES; - } - - /* res = R mod m */ - if ((err = mp_mod(&res, P, &res)) != MP_OKAY) { - goto __RES; - } - - /* create M table - * - * The M table contains powers of the input base, e.g. M[x] = G^x mod P - * - * The first half of the table is not computed though accept for M[0] and M[1] - */ - if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { - goto __RES; - } - - /* now set M[1] to G * R mod m */ - 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; - } - - for (x = 0; x < (winsize-1); x++) { - if ((err = mp_sqr(&M[1<<(winsize-1)], &M[1<<(winsize-1)])) != MP_OKAY) { - goto __RES; - } - if ((err = mp_montgomery_reduce(&M[1<<(winsize-1)], P, mp)) != MP_OKAY) { - goto __RES; - } - } - - /* create upper table */ - for (x = (1<<(winsize-1))+1; x < (1 << winsize); x++) { - if ((err = mp_mul(&M[x-1], &M[1], &M[x])) != MP_OKAY) { - goto __RES; - } - if ((err = mp_montgomery_reduce(&M[x], P, mp)) != MP_OKAY) { - goto __RES; - } - } - - /* set initial mode and bit cnt */ - mode = 0; - bitcnt = 0; - buf = 0; - digidx = X->used - 1; - bitcpy = bitbuf = 0; - - bitcnt = 1; - for (;;) { - /* grab next digit as required */ - if (--bitcnt == 0) { - if (digidx == -1) { - break; - } - buf = X->dp[digidx--]; - bitcnt = (int)DIGIT_BIT; - } - - /* grab the next msb from the exponent */ - y = (buf >> (DIGIT_BIT - 1)) & 1; - buf <<= 1; - - /* if the bit is zero and mode == 0 then we ignore it - * These represent the leading zero bits before the first 1 bit - * in the exponent. Technically this opt is not required but it - * does lower the # of trivial squaring/reductions used - */ - if (mode == 0 && y == 0) continue; - - /* if the bit is zero and mode == 1 then we square */ - if (y == 0 && mode == 1) { - if ((err = mp_sqr(&res, &res)) != MP_OKAY) { - goto __RES; - } - if ((err = mp_montgomery_reduce(&res, P, mp)) != MP_OKAY) { - goto __RES; - } - continue; - } - - /* else we add it to the window */ - bitbuf |= (y<<(winsize-++bitcpy)); - mode = 2; - - if (bitcpy == winsize) { - /* ok window is filled so square as required and multiply multiply */ - /* square first */ - for (x = 0; x < winsize; x++) { - if ((err = mp_sqr(&res, &res)) != MP_OKAY) { - goto __RES; - } - if ((err = mp_montgomery_reduce(&res, P, mp)) != MP_OKAY) { - goto __RES; - } - } - - /* then multiply */ - if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { - goto __RES; - } - if ((err = mp_montgomery_reduce(&res, P, mp)) != MP_OKAY) { - goto __RES; - } - - /* empty window and reset */ - bitcpy = bitbuf = 0; - mode = 1; - } - } - - /* if bits remain then square/multiply */ - if (mode == 2 && bitcpy > 0) { - /* square then multiply if the bit is set */ - for (x = 0; x < bitcpy; x++) { - if ((err = mp_sqr(&res, &res)) != MP_OKAY) { - goto __RES; - } - if ((err = mp_montgomery_reduce(&res, P, mp)) != MP_OKAY) { - goto __RES; - } - - bitbuf <<= 1; - if ((bitbuf & (1<used > 4 && P->used < MONTGOMERY_EXPT_CUTOFF) { - err = mp_exptmod_fast(G, X, P, Y); - DECFUNC(); - return err; - } - - /* find window size */ - x = mp_count_bits(X); - 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 - 1; - bitcpy = bitbuf = 0; - - bitcnt = 1; - for (;;) { - /* grab next digit as required */ - if (--bitcnt == 0) { - if (digidx == -1) { - break; - } - buf = X->dp[digidx--]; - bitcnt = (int)DIGIT_BIT; - } - - /* grab the next msb from the exponent */ - y = (buf >> (DIGIT_BIT - 1)) & 1; - buf <<= 1; - - /* if the bit is zero and mode == 0 then we ignore it - * These represent the leading zero bits before the first 1 bit - * in the exponent. Technically this opt is not required but it - * does lower the # of trivial squaring/reductions used - */ - if (y == 0 && mode == 0) continue; - - /* if the bit is zero and mode == 1 then we square */ - if (y == 0 && mode == 1) { - if ((err = mp_sqr(&res, &res)) != MP_OKAY) { - goto __RES; - } - if ((err = mp_reduce(&res, P, &mu)) != MP_OKAY) { - goto __RES; - } - continue; - } - - /* else we add it to the window */ - bitbuf |= (y<<(winsize-++bitcpy)); - mode = 2; - - if (bitcpy == winsize) { - /* ok window is filled so square as required and multiply multiply */ - /* square first */ - for (x = 0; x < winsize; x++) { - if ((err = mp_sqr(&res, &res)) != MP_OKAY) { - goto __RES; - } - if ((err = mp_reduce(&res, P, &mu)) != MP_OKAY) { - goto __RES; - } - } - - /* then multiply */ - if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { - goto __MU; - } - if ((err = mp_reduce(&res, P, &mu)) != MP_OKAY) { - goto __MU; - } - - /* empty window and reset */ - bitcpy = bitbuf = 0; - mode = 1; - } - } - - /* if bits remain then square/multiply */ - if (mode == 2 && bitcpy > 0) { - /* square then multiply if the bit is set */ - for (x = 0; x < bitcpy; x++) { - if ((err = mp_sqr(&res, &res)) != MP_OKAY) { - goto __RES; - } - if ((err = mp_reduce(&res, P, &mu)) != MP_OKAY) { - goto __RES; - } - - bitbuf <<= 1; - if ((bitbuf & (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 - */ -int mp_n_root(mp_int *a, mp_digit b, mp_int *c) -{ - mp_int t1, t2, t3; - int res, neg; - - /* input must be positive if b is even*/ - if ((b&1) == 0 && a->sign == MP_NEG) { - return MP_VAL; - } - - if ((res = mp_init(&t1)) != MP_OKAY) { - return res; - } - - if ((res = mp_init(&t2)) != MP_OKAY) { - goto __T1; - } - - if ((res = mp_init(&t3)) != MP_OKAY) { - goto __T2; - } - - /* if a is negative fudge the sign but keep track */ - neg = a->sign; - a->sign = MP_ZPOS; - - /* t2 = 2 */ - mp_set(&t2, 2); - - do { - /* t1 = t2 */ - if ((res = mp_copy(&t2, &t1)) != MP_OKAY) { - goto __T3; - } - - /* t2 = t1 - ((t1^b - a) / (b * t1^(b-1))) */ - if ((res = mp_expt_d(&t1, b-1, &t3)) != MP_OKAY) { /* t3 = t1^(b-1) */ - goto __T3; - } - - /* numerator */ - if ((res = mp_mul(&t3, &t1, &t2)) != MP_OKAY) { /* t2 = t1^b */ - goto __T3; - } - - if ((res = mp_sub(&t2, a, &t2)) != MP_OKAY) { /* t2 = t1^b - a */ - goto __T3; - } - - if ((res = mp_mul_d(&t3, b, &t3)) != MP_OKAY) { /* t3 = t1^(b-1) * b */ - goto __T3; - } - - if ((res = mp_div(&t2, &t3, &t3, NULL)) != MP_OKAY) { /* t3 = (t1^b - a)/(b * t1^(b-1)) */ - goto __T3; - } - - if ((res = mp_sub(&t1, &t3, &t2)) != MP_OKAY) { - goto __T3; - } - } while (mp_cmp(&t1, &t2) != MP_EQ); - - /* result can be off by a few so check */ - for (;;) { - if ((res = mp_expt_d(&t1, b, &t2)) != MP_OKAY) { - goto __T3; - } - - if (mp_cmp(&t2, a) == MP_GT) { - if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) { - goto __T3; - } - } else { - break; - } - } - - /* reset the sign of a first */ - a->sign = neg; - - /* set the result */ - mp_exch(&t1, c); - - /* set the sign of the result */ - c->sign = neg; - - res = MP_OKAY; - -__T3: mp_clear(&t3); -__T2: mp_clear(&t2); -__T1: mp_clear(&t1); - return res; -} - -/* computes the jacobi c = (a | n) (or Legendre if b is prime) - * HAC pp. 73 Algorithm 2.149 - */ -int mp_jacobi(mp_int *a, mp_int *n, int *c) -{ - mp_int a1, n1, e; - int s, r, res; - mp_digit residue; - - /* step 1. if a == 0, return 0 */ - if (mp_iszero(a) == 1) { - *c = 0; - return MP_OKAY; - } - - /* step 2. if a == 1, return 1 */ - if (mp_cmp_d(a, 1) == MP_EQ) { - *c = 1; - return MP_OKAY; - } - - /* default */ - s = 0; - - /* step 3. write a = a1 * 2^e */ - if ((res = mp_init_copy(&a1, a)) != MP_OKAY) { - return res; - } - - if ((res = mp_init(&n1)) != MP_OKAY) { - goto __A1; - } - - if ((res = mp_init(&e)) != MP_OKAY) { - goto __N1; - } - - while (mp_iseven(&a1) == 1) { - if ((res = mp_add_d(&e, 1, &e)) != MP_OKAY) { - goto __E; - } - - if ((res = mp_div_2(&a1, &a1)) != MP_OKAY) { - goto __E; - } - } - - /* step 4. if e is even set s=1 */ - if (mp_iseven(&e) == 1) { - s = 1; - } else { - /* else set s=1 if n = 1/7 (mod 8) or s=-1 if n = 3/5 (mod 8) */ - if ((res = mp_mod_d(n, 8, &residue)) != MP_OKAY) { - goto __E; - } - - if (residue == 1 || residue == 7) { - s = 1; - } else if (residue == 3 || residue == 5) { - s = -1; - } - } - - /* step 5. if n == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ - if ((res = mp_mod_d(n, 4, &residue)) != MP_OKAY) { - goto __E; - } - if (residue == 3) { - if ((res = mp_mod_d(&a1, 4, &residue)) != MP_OKAY) { - goto __E; - } - if (residue == 3) { - s = -s; - } - } - - /* if a1 == 1 we're done */ - if (mp_cmp_d(&a1, 1) == MP_EQ) { - *c = s; - } else { - /* n1 = n mod a1 */ - if ((res = mp_mod(n, &a1, &n1)) != MP_OKAY) { - goto __E; - } - if ((res = mp_jacobi(&n1, &a1, &r)) != MP_OKAY) { - goto __E; - } - *c = s * r; - } - - /* done */ - res = MP_OKAY; -__E: mp_clear(&e); -__N1: mp_clear(&n1); -__A1: mp_clear(&a1); - return res; -} - -/* --> radix conversion <-- */ -/* reverse an array, used for radix code */ -static void reverse(unsigned char *s, int len) -{ - int ix, iy; - unsigned char t; - - ix = 0; - iy = len - 1; - while (ix < iy) { - t = s[ix]; s[ix] = s[iy]; s[iy] = t; - ++ix; - --iy; - } -} - -/* returns the number of bits in an int */ -int mp_count_bits(mp_int *a) -{ - int r; - mp_digit q; - - if (a->used == 0) { - return 0; - } - - r = (a->used - 1) * DIGIT_BIT; - q = a->dp[a->used - 1]; - while (q) { - ++r; - q >>= ((mp_digit)1); - } - return r; -} - -/* reads a unsigned char array, assumes the msb is stored first [big endian] */ -int mp_read_unsigned_bin(mp_int *a, unsigned char *b, int c) -{ - int res; - mp_zero(a); - while (c-- > 0) { - if ((res = mp_mul_2d(a, 8, a)) != MP_OKAY) { - return res; - } - - if (DIGIT_BIT != 7) { - a->dp[0] |= *b++; - a->used += 1; - } else { - a->dp[0] = (*b & MP_MASK); - a->dp[1] |= ((*b++ >> 7U) & 1); - a->used += 2; - } - } - mp_clamp(a); - return MP_OKAY; -} - -/* read signed bin, big endian, first byte is 0==positive or 1==negative */ -int mp_read_signed_bin(mp_int *a, unsigned char *b, int c) -{ - int res; - - if ((res = mp_read_unsigned_bin(a, b + 1, c - 1)) != MP_OKAY) { - return res; - } - a->sign = ((b[0] == (unsigned char)0) ? MP_ZPOS : MP_NEG); - return MP_OKAY; -} - -/* store in unsigned [big endian] format */ -int mp_to_unsigned_bin(mp_int *a, unsigned char *b) -{ - int x, res; - mp_int t; - - if ((res = mp_init_copy(&t, a)) != MP_OKAY) { - return res; - } - - x = 0; - while (mp_iszero(&t) == 0) { - if (DIGIT_BIT != 7) { - b[x++] = (unsigned char)(t.dp[0] & 255); - } else { - b[x++] = (unsigned char)(t.dp[0] | ((t.dp[1] & 0x01) << 7)); - } - if ((res = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) { - mp_clear(&t); - return res; - } - } - reverse(b, x); - mp_clear(&t); - return MP_OKAY; -} - -/* store in signed [big endian] format */ -int mp_to_signed_bin(mp_int *a, unsigned char *b) -{ - int res; - - if ((res = mp_to_unsigned_bin(a, b+1)) != MP_OKAY) { - return res; - } - b[0] = (unsigned char)((a->sign == MP_ZPOS) ? 0 : 1); - return MP_OKAY; -} - -/* get the size for an unsigned equivalent */ -int mp_unsigned_bin_size(mp_int *a) -{ - int size = mp_count_bits(a); - return (size/8 + ((size&7) != 0 ? 1 : 0)); -} - -/* get the size for an signed equivalent */ -int mp_signed_bin_size(mp_int *a) -{ - return 1 + mp_unsigned_bin_size(a); -} - -/* read a string [ASCII] in a given radix */ -int mp_read_radix(mp_int *a, char *str, int radix) -{ - int y, res, neg; - char ch; - - if (radix < 2 || radix > 64) { - return MP_VAL; - } - - if (*str == '-') { - ++str; - neg = MP_NEG; - } else { - neg = MP_ZPOS; - } - - mp_zero(a); - while (*str) { - ch = (char)((radix < 36) ? toupper(*str) : *str); - for (y = 0; y < 64; y++) { - if (ch == s_rmap[y]) { - break; - } - } - - if (y < radix) { - if ((res = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) { - return res; - } - if ((res = mp_add_d(a, (mp_digit)y, a)) != MP_OKAY) { - return res; - } - } else { - break; - } - ++str; - } - a->sign = neg; - return MP_OKAY; -} - -/* stores a bignum as a ASCII string in a given radix (2..64) */ -int mp_toradix(mp_int *a, char *str, int radix) -{ - int res, digs; - mp_int t; - mp_digit d; - char *_s = str; - - if (radix < 2 || radix > 64) { - return MP_VAL; - } - - if ((res = mp_init_copy(&t, a)) != MP_OKAY) { - return res; - } - - if (t.sign == MP_NEG) { - ++_s; - *str++ = '-'; - t.sign = MP_ZPOS; - } - - digs = 0; - while (mp_iszero(&t) == 0) { - if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { - mp_clear(&t); - return res; - } - *str++ = s_rmap[d]; - ++digs; - } - reverse((unsigned char *)_s, digs); - *str++ = '\0'; - mp_clear(&t); - return MP_OKAY; -} - -/* returns size of ASCII reprensentation */ -int mp_radix_size(mp_int *a, int radix) -{ - int res, digs; - mp_int t; - mp_digit d; - - /* special case for binary */ - if (radix == 2) { - return mp_count_bits(a) + (a->sign == MP_NEG ? 1 : 0) + 1; - } - - if (radix < 2 || radix > 64) { - return 0; - } - - if ((res = mp_init_copy(&t, a)) != MP_OKAY) { - return 0; - } - - digs = 0; - if (t.sign == MP_NEG) { - ++digs; - t.sign = MP_ZPOS; - } - - while (mp_iszero(&t) == 0) { - if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { - mp_clear(&t); - return 0; - } - ++digs; - } - mp_clear(&t); - return digs + 1; -} - diff --git a/bn.pdf b/bn.pdf index b8152e1c1a5b92cb67feddb0c598a6b3794a73f7..df69417ba1b7d4d0441ad1abefee794c6f1e84af 100644 GIT binary patch delta 121260 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

Mw^d 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≶trLE6 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%0D@oQl6-? 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¬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¨ONM?U%AHQRoPIM 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 diff --git a/bn.tex b/bn.tex index 5c8b73e..b5f0227 100644 --- a/bn.tex +++ b/bn.tex @@ -1,7 +1,7 @@ \documentclass{article} \begin{document} -\title{LibTomMath v0.11 \\ A Free Multiple Precision Integer Library} +\title{LibTomMath v0.12 \\ A Free Multiple Precision Integer Library} \author{Tom St Denis \\ tomstdenis@iahu.ca} \maketitle \newpage @@ -35,23 +35,24 @@ 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. +As of v0.12 LibTomMath is not a simple single source file project like MPI. LibTomMath retains the exact same API as MPI +but is implemented differently. To build LibTomMath you will need a copy of GNU cc and GNU make. Both are free so if you +don't have a copy don't whine to me about it. -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 +To build the library type \begin{verbatim} -#include "mpi.h" +make \end{verbatim} -with +This will build the library file libtommath.a. If you want to build the library and also install it (in /usr/bin and /usr/include) then +type \begin{verbatim} -#include "bn.h" +make install \end{verbatim} -Remove ``mpi.c'' from your project and replace it with ``bn.c''. +Now within your application include ``tommath.h'' and link against libtommath.a to get MPI-like functionality. \section{Programming with LibTomMath} @@ -183,6 +184,28 @@ 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); + +/* makes a pseudo-random int of a given size */ +int mp_rand(mp_int *a, int digits); + +\end{verbatim} + +\subsection{Binary Operations} + +\begin{verbatim} + +/* c = a XOR b */ +int mp_xor(mp_int *a, mp_int *b, mp_int *c); + +/* c = a OR b */ +int mp_or(mp_int *a, mp_int *b, mp_int *c); + +/* c = a AND b */ +int mp_and(mp_int *a, mp_int *b, mp_int *c); + \end{verbatim} \subsection{Basic Arithmetic} @@ -366,6 +389,27 @@ 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\_2expt(mp\_int *a, int b)} +Computes $a = 2^b$ by first setting $a$ to zero then OR'ing the correct bit to get the right value. + +\subsubsection{mp\_rand(mp\_int *a, int digits)} +Computes a pseudo-random (\textit{via rand()}) integer that is always ``$digits$'' digits in length. Not for +cryptographic use. + +\subsection{Binary Arithmetic} +\subsubsection{mp\_xor(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes $c = a \oplus b$, pseudo-extends with zeroes whichever of $a$ or $b$ is shorter such that the length +of $c$ is the maximum length of the two inputs. + +\subsubsection{mp\_or(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes $c = a \lor b$, pseudo-extends with zeroes whichever of $a$ or $b$ is shorter such that the length +of $c$ is the maximum length of the two inputs. + +\subsubsection{mp\_and(mp\_int *a, mp\_int *b, mp\_int *c)} +Computes $c = a \land b$, pseudo-extends with zeroes whichever of $a$ or $b$ is shorter such that the length +of $c$ is the maximum length of the two inputs. + + \subsection{Basic Arithmetic} \subsubsection{mp\_cmp(mp\_int *a, mp\_int *b)} @@ -622,14 +666,13 @@ of multiplications. Consider a 512-bit exponent. The worst case for the LibTom 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. +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. - - +LibTomMath also has a variant of the exptmod function that uses Montgomery reductions instead of Barrett reductions +which is faser. As a result of all these changes exponentiation in LibTomMath is much faster than compared to MPI. \end{document} diff --git a/bn_fast_mp_invmod.c b/bn_fast_mp_invmod.c new file mode 100644 index 0000000..e1dcce3 --- /dev/null +++ b/bn_fast_mp_invmod.c @@ -0,0 +1,168 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* computes the modular inverse via binary extended euclidean algorithm, that is c = 1/a mod b */ +int +fast_mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, B, D; + int res, neg; + + + if ((res = mp_init (&x)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_init (&y)) != MP_OKAY) { + goto __X; + } + + if ((res = mp_init (&u)) != MP_OKAY) { + goto __Y; + } + + if ((res = mp_init (&v)) != MP_OKAY) { + goto __U; + } + + if ((res = mp_init (&B)) != MP_OKAY) { + goto __V; + } + + if ((res = mp_init (&D)) != MP_OKAY) { + goto __B; + } + + /* x == modulus, y == value to invert */ + if ((res = mp_copy (b, &x)) != MP_OKAY) { + goto __D; + } + if ((res = mp_copy (a, &y)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_abs (&y, &y)) != MP_OKAY) { + goto __D; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto __D; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto __D; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto __D; + } + mp_set (&D, 1); + + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto __D; + } + /* 4.2 if A or B is odd then */ + if (mp_iseven (&B) == 0) { + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto __D; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto __D; + } + } + + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto __D; + } + /* 5.2 if C,D are even then */ + if (mp_iseven (&D) == 0) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto __D; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto __D; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto __D; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto __D; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto __D; + } + + /* b is now the inverse */ + neg = a->sign; + while (D.sign == MP_NEG) { + if ((res = mp_add (&D, b, &D)) != MP_OKAY) { + goto __D; + } + } + mp_exch (&D, c); + c->sign = neg; + res = MP_OKAY; + +__D:mp_clear (&D); +__B:mp_clear (&B); +__V:mp_clear (&v); +__U:mp_clear (&u); +__Y:mp_clear (&y); +__X:mp_clear (&x); +__ERR: + return res; +} diff --git a/bn_fast_mp_montgomery_reduce.c b/bn_fast_mp_montgomery_reduce.c new file mode 100644 index 0000000..dbc3478 --- /dev/null +++ b/bn_fast_mp_montgomery_reduce.c @@ -0,0 +1,116 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* computes xR^-1 == x (mod N) via Montgomery Reduction (comba) */ +int +fast_mp_montgomery_reduce (mp_int * a, mp_int * m, mp_digit mp) +{ + int ix, res, olduse; + mp_word W[512]; + + /* get old used count */ + olduse = a->used; + + /* grow a as required */ + if (a->alloc < m->used + 1) { + if ((res = mp_grow (a, m->used + 1)) != MP_OKAY) { + return res; + } + } + + /* 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 + * + * 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) + */ + register mp_digit ui; + ui = (((mp_digit) (W[ix] & MP_MASK)) * mp) & MP_MASK; + + /* 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; + register mp_word *_W; + + /* aliases */ + 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 next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* nox fix rest of carries */ + for (++ix; ix <= m->used * 2 + 1; ix++) { + W[ix] += (W[ix - 1] >> ((mp_word) DIGIT_BIT)); + } + + /* 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] & ((mp_word) MP_MASK); + } + + /* set the max used */ + a->used = m->used + 1; + + /* 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; + } + mp_clamp (a); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (a, m) != MP_LT) { + return s_mp_sub (a, m, a); + } + return MP_OKAY; +} diff --git a/bn_fast_s_mp_mul_digs.c b/bn_fast_s_mp_mul_digs.c new file mode 100644 index 0000000..c433dd7 --- /dev/null +++ b/bn_fast_s_mp_mul_digs.c @@ -0,0 +1,113 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* Fast (comba) multiplier + * + * This is the fast column-array [comba] multiplier. It is designed to compute + * the columns of the product first then handle the carries afterwards. This + * has the effect of making the nested loops that compute the columns very + * simple and schedulable on super-scalar processors. + * + */ +int +fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int olduse, res, pa, ix; + mp_word W[512]; + + if (c->alloc < digs) { + if ((res = mp_grow (c, digs)) != MP_OKAY) { + return res; + } + } + + /* clear temp buf (the columns) */ + memset (W, 0, sizeof (mp_word) * digs); + + /* calculate the columns */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + + /* this multiplier has been modified to allow you to control how many digits + * of output are produced. So at most we want to make upto "digs" digits + * of output + */ + + + /* this adds products to distinct columns (at ix+iy) of W + * note that each step through the loop is not dependent on + * the previous which means the compiler can easily unroll + * the loop without scheduling problems + */ + { + register mp_digit tmpx, *tmpy; + register mp_word *_W; + register int iy, pb; + + /* alias for the the word on the left e.g. A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for the right side */ + tmpy = b->dp; + + /* alias for the columns, each step through the loop adds a new + term to each column + */ + _W = W + ix; + + /* the number of digits is limited by their placement. E.g. + we avoid multiplying digits that will end up above the # of + digits of precision requested + */ + pb = MIN (b->used, digs - ix); + + for (iy = 0; iy < pb; iy++) { + *_W++ += ((mp_word) tmpx) * ((mp_word) * tmpy++); + } + } + + } + + /* setup dest */ + olduse = c->used; + c->used = digs; + + + /* At this point W[] contains the sums of each column. To get the + * correct result we must take the extra bits from each column and + * carry them down + * + * Note that while this adds extra code to the multiplier it saves time + * since the carry propagation is removed from the above nested loop. + * This has the effect of reducing the work from N*(N+N*c)==N^2 + c*N^2 to + * N^2 + N*c where c is the cost of the shifting. On very small numbers + * this is slower but on most cryptographic size numbers it is faster. + */ + + for (ix = 1; ix < digs; 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[digs - 1] = (mp_digit) (W[digs - 1] & ((mp_word) MP_MASK)); + + /* clear unused */ + for (; ix < olduse; ix++) { + c->dp[ix] = 0; + } + + mp_clamp (c); + return MP_OKAY; +} diff --git a/bn_fast_s_mp_mul_high_digs.c b/bn_fast_s_mp_mul_high_digs.c new file mode 100644 index 0000000..cc30386 --- /dev/null +++ b/bn_fast_s_mp_mul_high_digs.c @@ -0,0 +1,86 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* this is a modified version of fast_s_mp_mul_digs that only produces + * output digits *above* digs. See the comments for fast_s_mp_mul_digs + * to see how it works. + * + * This is used in the Barrett reduction since for one of the multiplications + * only the higher digits were needed. This essentially halves the work. + */ +int +fast_s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + int oldused, newused, res, pa, pb, ix; + mp_word W[512]; + + + newused = a->used + b->used + 1; + if (c->alloc < newused) { + if ((res = mp_grow (c, newused)) != MP_OKAY) { + return res; + } + } + + /* like the other comba method we compute the columns first */ + pa = a->used; + pb = b->used; + memset (&W[digs], 0, (pa + pb + 1 - digs) * sizeof (mp_word)); + for (ix = 0; ix < pa; ix++) { + { + register mp_digit tmpx, *tmpy; + register int iy; + register mp_word *_W; + + /* work todo, that is we only calculate digits that are at "digs" or above */ + iy = digs - ix; + + /* copy of word on the left of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias for right side */ + tmpy = b->dp + iy; + + /* alias for the columns of output. Offset to be equal to or above the + * smallest digit place requested + */ + _W = &(W[digs]); + + /* compute column products for digits above the minimum */ + for (; iy < pb; iy++) { + *_W++ += ((mp_word) tmpx) * ((mp_word) * tmpy++); + } + } + } + + /* setup dest */ + oldused = c->used; + c->used = newused; + + /* now convert the array W downto what we need */ + 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 < oldused; ix++) { + c->dp[ix] = 0; + } + mp_clamp (c); + return MP_OKAY; +} diff --git a/bn_fast_s_mp_sqr.c b/bn_fast_s_mp_sqr.c new file mode 100644 index 0000000..c4f786d --- /dev/null +++ b/bn_fast_s_mp_sqr.c @@ -0,0 +1,112 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* fast squaring + * + * This is the comba method where the columns of the product are computed first + * then the carries are computed. This has the effect of making a very simple + * inner loop that is executed the most + * + * W2 represents the outer products and W the inner. + * + * A further optimizations is made because the inner products are of the form + * "A * B * 2". The *2 part does not need to be computed until the end which is + * good because 64-bit shifts are slow! + * + * + */ +int +fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, newused, res, ix, pa; + mp_word W2[512], W[512]; + + pa = a->used; + newused = pa + pa + 1; + if (b->alloc < newused) { + if ((res = mp_grow (b, newused)) != MP_OKAY) { + return res; + } + } + + /* zero temp buffer (columns) + * Note that there are two buffers. Since squaring requires + * a outter and inner product and the inner product requires + * computing a product and doubling it (a relatively expensive + * op to perform n^2 times if you don't have to) the inner and + * outer products are computed in different buffers. This way + * the inner product can be doubled using n doublings instead of + * n^2 + */ + memset (W, 0, newused * sizeof (mp_word)); + memset (W2, 0, newused * sizeof (mp_word)); + + /* This computes the inner product. To simplify the inner N^2 loop + * the multiplication by two is done afterwards in the N loop. + */ + for (ix = 0; ix < pa; ix++) { + /* compute the outer product */ + W2[ix + ix] += ((mp_word) a->dp[ix]) * ((mp_word) a->dp[ix]); + + { + register mp_digit tmpx, *tmpy; + register mp_word *_W; + register int iy; + + /* copy of left side */ + tmpx = a->dp[ix]; + + /* alias for right side */ + tmpy = a->dp + (ix + 1); + + /* the column to store the result in */ + _W = W + (ix + ix + 1); + + /* inner products */ + for (iy = ix + 1; iy < pa; iy++) { + *_W++ += ((mp_word) tmpx) * ((mp_word) * tmpy++); + } + } + } + + /* setup dest */ + olduse = b->used; + b->used = newused; + + /* double first value, since the inner products are half of what they should be */ + W[0] += W[0] + W2[0]; + + /* now compute digits */ + 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[(newused) - 1] = (mp_digit) (W[(newused) - 1] & ((mp_word) MP_MASK)); + + /* clear high */ + for (; ix < olduse; ix++) { + b->dp[ix] = 0; + } + + /* fix the sign (since we no longer make a fresh temp) */ + b->sign = MP_ZPOS; + + mp_clamp (b); + return MP_OKAY; +} diff --git a/bn_mp_2expt.c b/bn_mp_2expt.c new file mode 100644 index 0000000..ace1b99 --- /dev/null +++ b/bn_mp_2expt.c @@ -0,0 +1,31 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* computes a = 2^b */ +int +mp_2expt (mp_int * a, int b) +{ + int res; + + mp_zero (a); + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + a->used = b / DIGIT_BIT + 1; + a->dp[b / DIGIT_BIT] = 1 << (b % DIGIT_BIT); + + return MP_OKAY; +} diff --git a/bn_mp_abs.c b/bn_mp_abs.c new file mode 100644 index 0000000..299d3b6 --- /dev/null +++ b/bn_mp_abs.c @@ -0,0 +1,27 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* b = |a| */ +int +mp_abs (mp_int * a, mp_int * b) +{ + int res; + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + b->sign = MP_ZPOS; + return MP_OKAY; +} diff --git a/bn_mp_add.c b/bn_mp_add.c new file mode 100644 index 0000000..a2ad4fd --- /dev/null +++ b/bn_mp_add.c @@ -0,0 +1,56 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* high level addition (handles signs) */ +int +mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + + sa = a->sign; + sb = b->sign; + + /* handle four cases */ + if (sa == MP_ZPOS && sb == MP_ZPOS) { + /* both positive */ + res = s_mp_add (a, b, c); + c->sign = MP_ZPOS; + } else if (sa == MP_ZPOS && sb == MP_NEG) { + /* a + -b == a - b, but if b>a then we do it as -(b-a) */ + if (mp_cmp_mag (a, b) == MP_LT) { + res = s_mp_sub (b, a, c); + c->sign = MP_NEG; + } else { + res = s_mp_sub (a, b, c); + c->sign = MP_ZPOS; + } + } else if (sa == MP_NEG && sb == MP_ZPOS) { + /* -a + b == b - a, but if a>b then we do it as -(a-b) */ + if (mp_cmp_mag (a, b) == MP_GT) { + res = s_mp_sub (a, b, c); + c->sign = MP_NEG; + } else { + res = s_mp_sub (b, a, c); + c->sign = MP_ZPOS; + } + } else { + /* -a + -b == -(a + b) */ + res = s_mp_add (a, b, c); + c->sign = MP_NEG; + } + return res; +} diff --git a/bn_mp_add_d.c b/bn_mp_add_d.c new file mode 100644 index 0000000..0391bc1 --- /dev/null +++ b/bn_mp_add_d.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* single digit addition */ +int +mp_add_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_int t; + int res; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + mp_set (&t, b); + res = mp_add (a, &t, c); + + mp_clear (&t); + return res; +} diff --git a/bn_mp_addmod.c b/bn_mp_addmod.c new file mode 100644 index 0000000..adce5d5 --- /dev/null +++ b/bn_mp_addmod.c @@ -0,0 +1,36 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* d = a + b (mod c) */ +int +mp_addmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_add (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} diff --git a/bn_mp_and.c b/bn_mp_and.c new file mode 100644 index 0000000..d153c44 --- /dev/null +++ b/bn_mp_and.c @@ -0,0 +1,51 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* AND two ints together */ +int +mp_and (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] &= x->dp[ix]; + } + + /* zero digits above the last from the smallest mp_int */ + for (; ix < t.used; ix++) { + t.dp[ix] = 0; + } + + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_mp_clamp.c b/bn_mp_clamp.c new file mode 100644 index 0000000..77f3e1a --- /dev/null +++ b/bn_mp_clamp.c @@ -0,0 +1,26 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* trim unused digits */ +void +mp_clamp (mp_int * a) +{ + while (a->used > 0 && a->dp[a->used - 1] == 0) + --(a->used); + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} diff --git a/bn_mp_clear.c b/bn_mp_clear.c new file mode 100644 index 0000000..e88e95a --- /dev/null +++ b/bn_mp_clear.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* clear one (frees) */ +void +mp_clear (mp_int * a) +{ + if (a->dp != NULL) { + + /* first zero the digits */ + memset (a->dp, 0, sizeof (mp_digit) * a->used); + + /* free ram */ + free (a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + } +} diff --git a/bn_mp_cmp.c b/bn_mp_cmp.c new file mode 100644 index 0000000..527d35c --- /dev/null +++ b/bn_mp_cmp.c @@ -0,0 +1,30 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* compare two ints (signed)*/ +int +mp_cmp (mp_int * a, mp_int * b) +{ + int res; + /* compare based on sign */ + if (a->sign == MP_NEG && b->sign == MP_ZPOS) { + return MP_LT; + } else if (a->sign == MP_ZPOS && b->sign == MP_NEG) { + return MP_GT; + } + res = mp_cmp_mag (a, b); + return res; +} diff --git a/bn_mp_cmp_d.c b/bn_mp_cmp_d.c new file mode 100644 index 0000000..1506b87 --- /dev/null +++ b/bn_mp_cmp_d.c @@ -0,0 +1,37 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* compare a digit */ +int +mp_cmp_d (mp_int * a, mp_digit b) +{ + + if (a->sign == MP_NEG) { + return MP_LT; + } + + if (a->used > 1) { + return MP_GT; + } + + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} diff --git a/bn_mp_cmp_mag.c b/bn_mp_cmp_mag.c new file mode 100644 index 0000000..efe5b2b --- /dev/null +++ b/bn_mp_cmp_mag.c @@ -0,0 +1,40 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* compare maginitude of two ints (unsigned) */ +int +mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } else if (a->used < b->used) { + return MP_LT; + } + + /* compare based on digits */ + for (n = a->used - 1; n >= 0; n--) { + if (a->dp[n] > b->dp[n]) { + return MP_GT; + } else if (a->dp[n] < b->dp[n]) { + return MP_LT; + } + } + return MP_EQ; +} diff --git a/bn_mp_copy.c b/bn_mp_copy.c new file mode 100644 index 0000000..a502620 --- /dev/null +++ b/bn_mp_copy.c @@ -0,0 +1,48 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* copy, b = a */ +int +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + + /* if dst == src do nothing */ + if (a == b || a->dp == b->dp) { + return MP_OKAY; + } + + /* grow dest */ + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + + /* zero b and copy the parameters over */ + b->used = a->used; + b->sign = a->sign; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + b->dp[n] = a->dp[n]; + } + + /* clear high digits */ + for (n = b->used; n < b->alloc; n++) { + b->dp[n] = 0; + } + return MP_OKAY; +} diff --git a/bn_mp_count_bits.c b/bn_mp_count_bits.c new file mode 100644 index 0000000..3f6ed28 --- /dev/null +++ b/bn_mp_count_bits.c @@ -0,0 +1,35 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* returns the number of bits in an int */ +int +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + if (a->used == 0) { + return 0; + } + + r = (a->used - 1) * DIGIT_BIT; + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} diff --git a/bn_mp_div.c b/bn_mp_div.c new file mode 100644 index 0000000..b324d2d --- /dev/null +++ b/bn_mp_div.c @@ -0,0 +1,200 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* integer signed division. c*b + d == a [e.g. a/b, c=quotient, d=remainder] + * HAC pp.598 Algorithm 14.20 + * + * Note that the description in HAC is horribly incomplete. For example, + * it doesn't consider the case where digits are removed from 'x' in the inner + * loop. It also doesn't consider the case that y has fewer than three digits, etc.. + * + * The overall algorithm is as described as 14.20 from HAC but fixed to treat these cases. +*/ +int +mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + mp_int q, x, y, t1, t2; + int res, n, t, i, norm, neg; + + + /* is divisor zero ? */ + if (mp_iszero (b) == 1) { + return MP_VAL; + } + + /* if a < b then q=0, r = a */ + if (mp_cmp_mag (a, b) == MP_LT) { + if (d != NULL) { + res = mp_copy (a, d); + } else { + res = MP_OKAY; + } + if (c != NULL) { + mp_zero (c); + } + return res; + } + + if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { + return res; + } + q.used = a->used + 2; + + if ((res = mp_init (&t1)) != MP_OKAY) { + goto __Q; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto __T1; + } + + if ((res = mp_init_copy (&x, a)) != MP_OKAY) { + goto __T2; + } + + if ((res = mp_init_copy (&y, b)) != MP_OKAY) { + goto __X; + } + + /* fix the sign */ + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2^DIGIT_BIT] */ + norm = 0; + while ((y.dp[y.used - 1] & (((mp_digit) 1) << (DIGIT_BIT - 1))) == + ((mp_digit) 0)) { + ++norm; + if ((res = mp_mul_2d (&x, 1, &x)) != MP_OKAY) { + goto __Y; + } + if ((res = mp_mul_2d (&y, 1, &y)) != MP_OKAY) { + goto __Y; + } + } + + /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ + n = x.used - 1; + t = y.used - 1; + + /* step 2. while (x >= y*b^n-t) do { q[n-t] += 1; x -= y*b^{n-t} } */ + if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b^{n-t} */ + goto __Y; + } + + while (mp_cmp (&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { + goto __Y; + } + } + + /* reset y by shifting it back down */ + mp_rshd (&y, n - t); + + /* step 3. for i from n down to (t + 1) */ + for (i = n; i >= (t + 1); i--) { + if (i > x.alloc) + continue; + + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ + if (x.dp[i] == y.dp[t]) { + q.dp[i - t - 1] = ((1UL << DIGIT_BIT) - 1UL); + } else { + mp_word tmp; + tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); + tmp |= ((mp_word) x.dp[i - 1]); + tmp /= ((mp_word) y.dp[t]); + if (tmp > (mp_word) MP_MASK) + tmp = MP_MASK; + q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); + } + + /* step 3.2 while (q{i-t-1} * (yt * b + y{t-1})) > xi * b^2 + xi-1 * b + xi-2 do q{i-t-1} -= 1; */ + q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; + do { + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; + + /* find left hand */ + mp_zero (&t1); + t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto __Y; + } + + /* find right hand */ + t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; + t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; + t2.dp[2] = x.dp[i]; + t2.used = 3; + } + while (mp_cmp (&t1, &t2) == MP_GT); + + /* step 3.3 x = x - q{i-t-1} * y * b^{i-t-1} */ + if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { + goto __Y; + } + + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto __Y; + } + + if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { + goto __Y; + } + + /* step 3.4 if x < 0 then { x = x + y*b^{i-t-1}; q{i-t-1} -= 1; } */ + if (x.sign == MP_NEG) { + if ((res = mp_copy (&y, &t1)) != MP_OKAY) { + goto __Y; + } + if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { + goto __Y; + } + if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { + goto __Y; + } + + q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; + } + } + + /* now q is the quotient and x is the remainder [which we have to normalize] */ + /* get sign before writing to c */ + x.sign = a->sign; + if (c != NULL) { + mp_clamp (&q); + mp_exch (&q, c); + c->sign = neg; + } + + if (d != NULL) { + mp_div_2d (&x, norm, &x, NULL); + mp_clamp (&x); + mp_exch (&x, d); + } + + res = MP_OKAY; + +__Y:mp_clear (&y); +__X:mp_clear (&x); +__T2:mp_clear (&t2); +__T1:mp_clear (&t1); +__Q:mp_clear (&q); + return res; +} diff --git a/bn_mp_div_2.c b/bn_mp_div_2.c new file mode 100644 index 0000000..bc7dc28 --- /dev/null +++ b/bn_mp_div_2.c @@ -0,0 +1,38 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* b = a/2 */ +int +mp_div_2 (mp_int * a, mp_int * b) +{ + mp_digit r, rr; + int x, res; + + + /* copy */ + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + + r = 0; + for (x = b->used - 1; x >= 0; x--) { + rr = b->dp[x] & 1; + b->dp[x] = (b->dp[x] >> 1) | (r << (DIGIT_BIT - 1)); + r = rr; + } + mp_clamp (b); + return MP_OKAY; +} diff --git a/bn_mp_div_2d.c b/bn_mp_div_2d.c new file mode 100644 index 0000000..3bfa5aa --- /dev/null +++ b/bn_mp_div_2d.c @@ -0,0 +1,78 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* shift right by a certain bit count (store quotient in c, remainder in d) */ +int +mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) +{ + mp_digit D, r, rr; + int x, res; + mp_int t; + + + /* if the shift count is <= 0 then we do no work */ + if (b <= 0) { + res = mp_copy (a, c); + if (d != NULL) { + mp_zero (d); + } + return res; + } + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + /* get the remainder */ + if (d != NULL) { + if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + /* shift by as many digits in the bit count */ + mp_rshd (c, b / DIGIT_BIT); + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = c->dp[x] & ((mp_digit) ((1U << D) - 1U)); + + /* shift the current word and mix in the carry bits from the previous word */ + c->dp[x] = (c->dp[x] >> D) | (r << (DIGIT_BIT - D)); + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + res = MP_OKAY; + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_mp_div_d.c b/bn_mp_div_d.c new file mode 100644 index 0000000..321f030 --- /dev/null +++ b/bn_mp_div_d.c @@ -0,0 +1,44 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* single digit division */ +int +mp_div_d (mp_int * a, mp_digit b, mp_int * c, mp_digit * d) +{ + mp_int t, t2; + int res; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + mp_set (&t, b); + res = mp_div (a, &t, c, &t2); + + if (d != NULL) { + *d = t2.dp[0]; + } + + mp_clear (&t); + mp_clear (&t2); + return res; +} diff --git a/bn_mp_exch.c b/bn_mp_exch.c new file mode 100644 index 0000000..44e7087 --- /dev/null +++ b/bn_mp_exch.c @@ -0,0 +1,25 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +void +mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} diff --git a/bn_mp_expt_d.c b/bn_mp_expt_d.c new file mode 100644 index 0000000..80f9578 --- /dev/null +++ b/bn_mp_expt_d.c @@ -0,0 +1,49 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +int +mp_expt_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, x; + mp_int g; + + + if ((res = mp_init_copy (&g, a)) != MP_OKAY) { + return res; + } + + /* set initial result */ + mp_set (c, 1); + + for (x = 0; x < (int) DIGIT_BIT; x++) { + if ((res = mp_sqr (c, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + + if ((b & (mp_digit) (1 << (DIGIT_BIT - 1))) != 0) { + if ((res = mp_mul (c, &g, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + } + + b <<= 1; + } + + mp_clear (&g); + return MP_OKAY; +} diff --git a/bn_mp_exptmod.c b/bn_mp_exptmod.c new file mode 100644 index 0000000..ce81953 --- /dev/null +++ b/bn_mp_exptmod.c @@ -0,0 +1,213 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +int +mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + mp_int M[256], res, mu; + mp_digit buf; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + + /* if the modulus is odd use the fast method */ + if (mp_isodd (P) == 1 && P->used > 4 && P->used < MONTGOMERY_EXPT_CUTOFF) { + err = mp_exptmod_fast (G, X, P, Y); + return err; + } + + /* find window size */ + x = mp_count_bits (X); + 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 << winsize); x++) { + if ((err = mp_init_size (&M[x], 1)) != MP_OKAY) { + for (y = 0; y < x; y++) { + mp_clear (&M[y]); + } + return err; + } + } + + /* create mu, used for Barrett reduction */ + if ((err = mp_init (&mu)) != MP_OKAY) { + goto __M; + } + if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { + goto __MU; + } + + /* create M table + * + * The M table contains powers of the input base, e.g. M[x] = G^x mod P + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto __MU; + } + + /* 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 __MU; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = + mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto __MU; + } + if ((err = mp_reduce (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { + goto __MU; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto __MU; + } + if ((err = mp_reduce (&M[x], P, &mu)) != MP_OKAY) { + goto __MU; + } + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto __MU; + } + mp_set (&res, 1); + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 0; + buf = 0; + digidx = X->used - 1; + bitcpy = bitbuf = 0; + + bitcnt = 1; + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + if (digidx == -1) { + break; + } + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (DIGIT_BIT - 1)) & 1; + buf <<= 1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) + continue; + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_reduce (&res, P, &mu)) != MP_OKAY) { + goto __RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_reduce (&res, P, &mu)) != MP_OKAY) { + goto __RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto __MU; + } + if ((err = mp_reduce (&res, P, &mu)) != MP_OKAY) { + goto __MU; + } + + /* empty window and reset */ + bitcpy = bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_reduce (&res, P, &mu)) != MP_OKAY) { + goto __RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_reduce (&res, P, &mu)) != MP_OKAY) { + goto __RES; + } + } + } + } + + mp_exch (&res, Y); + err = MP_OKAY; +__RES:mp_clear (&res); +__MU:mp_clear (&mu); +__M: + for (x = 0; x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} diff --git a/bn_mp_exptmod_fast.c b/bn_mp_exptmod_fast.c new file mode 100644 index 0000000..bd25b3e --- /dev/null +++ b/bn_mp_exptmod_fast.c @@ -0,0 +1,229 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* computes Y == G^X mod P, HAC pp.616, Algorithm 14.85 + * + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. + * The value of k changes based on the size of the exponent. + * + * Uses Montgomery reduction + */ +int +mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + mp_int M[256], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* find window size */ + x = mp_count_bits (X); + 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 << winsize); x++) { + if ((err = mp_init_size (&M[x], 1)) != MP_OKAY) { + for (y = 0; y < x; y++) { + mp_clear (&M[y]); + } + return err; + } + } + + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto __M; + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto __RES; + } + + /* now we need R mod m */ + if ((err = mp_2expt (&res, P->used * DIGIT_BIT)) != MP_OKAY) { + goto __RES; + } + + /* res = R mod m (can use modified double/subtract ...) */ + if ((err = mp_mod (&res, P, &res)) != MP_OKAY) { + goto __RES; + } + + /* create M table + * + * The M table contains powers of the input base, e.g. M[x] = G^x mod P + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { + goto __RES; + } + + /* now set M[1] to G * R mod m */ + 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; + } + + for (x = 0; x < (winsize - 1); x++) { + if ((err = + mp_sqr (&M[1 << (winsize - 1)], + &M[1 << (winsize - 1)])) != MP_OKAY) { + goto __RES; + } + if ((err = + mp_montgomery_reduce (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* create upper table */ + for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { + if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { + goto __RES; + } + if ((err = mp_montgomery_reduce (&M[x], P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 0; + buf = 0; + digidx = X->used - 1; + bitcpy = bitbuf = 0; + + bitcnt = 1; + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + if (digidx == -1) { + break; + } + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (DIGIT_BIT - 1)) & 1; + buf <<= 1; + + /* if the bit is zero and mode == 0 then we ignore it + * These represent the leading zero bits before the first 1 bit + * in the exponent. Technically this opt is not required but it + * does lower the # of trivial squaring/reductions used + */ + if (mode == 0 && y == 0) + continue; + + /* if the bit is zero and mode == 1 then we square */ + if (mode == 1 && y == 0) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + continue; + } + + /* else we add it to the window */ + bitbuf |= (y << (winsize - ++bitcpy)); + mode = 2; + + if (bitcpy == winsize) { + /* ok window is filled so square as required and multiply multiply */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + + /* empty window and reset */ + bitcpy = bitbuf = 0; + mode = 1; + } + } + + /* if bits remain then square/multiply */ + if (mode == 2 && bitcpy > 0) { + /* square then multiply if the bit is set */ + for (x = 0; x < bitcpy; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto __RES; + } + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + } + } + + /* fixup result */ + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + + mp_exch (&res, Y); + err = MP_OKAY; +__RES:mp_clear (&res); +__M: + for (x = 0; x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} diff --git a/bn_mp_gcd.c b/bn_mp_gcd.c new file mode 100644 index 0000000..e2594b5 --- /dev/null +++ b/bn_mp_gcd.c @@ -0,0 +1,118 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* Greatest Common Divisor using the binary method [Algorithm B, page 338, vol2 of TAOCP] + */ +int +mp_gcd (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int u, v, t; + int k, res, neg; + + + /* either zero than gcd is the largest */ + if (mp_iszero (a) == 1 && mp_iszero (b) == 0) { + return mp_copy (b, c); + } + if (mp_iszero (a) == 0 && mp_iszero (b) == 1) { + return mp_copy (a, c); + } + if (mp_iszero (a) == 1 && mp_iszero (b) == 1) { + mp_set (c, 1); + return MP_OKAY; + } + + /* if both are negative they share (-1) as a common divisor */ + neg = (a->sign == b->sign) ? a->sign : MP_ZPOS; + + if ((res = mp_init_copy (&u, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init_copy (&v, b)) != MP_OKAY) { + goto __U; + } + + /* must be positive for the remainder of the algorithm */ + u.sign = v.sign = MP_ZPOS; + + if ((res = mp_init (&t)) != MP_OKAY) { + goto __V; + } + + /* B1. Find power of two */ + k = 0; + while ((u.dp[0] & 1) == 0 && (v.dp[0] & 1) == 0) { + ++k; + if ((res = mp_div_2d (&u, 1, &u, NULL)) != MP_OKAY) { + goto __T; + } + if ((res = mp_div_2d (&v, 1, &v, NULL)) != MP_OKAY) { + goto __T; + } + } + + /* B2. Initialize */ + if ((u.dp[0] & 1) == 1) { + if ((res = mp_copy (&v, &t)) != MP_OKAY) { + goto __T; + } + t.sign = MP_NEG; + } else { + if ((res = mp_copy (&u, &t)) != MP_OKAY) { + goto __T; + } + } + + do { + /* B3 (and B4). Halve t, if even */ + while (t.used != 0 && (t.dp[0] & 1) == 0) { + if ((res = mp_div_2d (&t, 1, &t, NULL)) != MP_OKAY) { + goto __T; + } + } + + /* B5. if t>0 then u=t otherwise v=-t */ + if (t.used != 0 && t.sign != MP_NEG) { + if ((res = mp_copy (&t, &u)) != MP_OKAY) { + goto __T; + } + } else { + if ((res = mp_copy (&t, &v)) != MP_OKAY) { + goto __T; + } + v.sign = (v.sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; + } + + /* B6. t = u - v, if t != 0 loop otherwise terminate */ + if ((res = mp_sub (&u, &v, &t)) != MP_OKAY) { + goto __T; + } + } + while (t.used != 0); + + if ((res = mp_mul_2d (&u, k, &u)) != MP_OKAY) { + goto __T; + } + + mp_exch (&u, c); + c->sign = neg; + res = MP_OKAY; +__T:mp_clear (&t); +__V:mp_clear (&u); +__U:mp_clear (&v); + return res; +} diff --git a/bn_mp_grow.c b/bn_mp_grow.c new file mode 100644 index 0000000..2a12369 --- /dev/null +++ b/bn_mp_grow.c @@ -0,0 +1,40 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* grow as required */ +int +mp_grow (mp_int * a, int size) +{ + int i, n; + + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + size += (MP_PREC * 2) - (size & (MP_PREC - 1)); /* ensure there are always at least MP_PREC digits extra on top */ + + a->dp = realloc (a->dp, sizeof (mp_digit) * size); + if (a->dp == NULL) { + return MP_MEM; + } + + n = a->alloc; + a->alloc = size; + for (i = n; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} diff --git a/bn_mp_init.c b/bn_mp_init.c new file mode 100644 index 0000000..024bfd9 --- /dev/null +++ b/bn_mp_init.c @@ -0,0 +1,35 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* init a new bigint */ +int +mp_init (mp_int * a) +{ + + /* allocate ram required and clear it */ + a->dp = calloc (sizeof (mp_digit), MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the used to zero, allocated digit to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} diff --git a/bn_mp_init_copy.c b/bn_mp_init_copy.c new file mode 100644 index 0000000..b3f25ee --- /dev/null +++ b/bn_mp_init_copy.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* creates "a" then copies b into it */ +int +mp_init_copy (mp_int * a, mp_int * b) +{ + int res; + + if ((res = mp_init (a)) != MP_OKAY) { + return res; + } + res = mp_copy (b, a); + return res; +} diff --git a/bn_mp_init_size.c b/bn_mp_init_size.c new file mode 100644 index 0000000..4dc46b0 --- /dev/null +++ b/bn_mp_init_size.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* init a mp_init and grow it to a given size */ +int +mp_init_size (mp_int * a, int size) +{ + + /* pad up so there are at least 16 zero digits */ + size += (MP_PREC * 2) - (size & (MP_PREC - 1)); /* ensure there are always at least 16 digits extra on top */ + a->dp = calloc (sizeof (mp_digit), size); + if (a->dp == NULL) { + return MP_MEM; + } + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + return MP_OKAY; +} diff --git a/bn_mp_invmod.c b/bn_mp_invmod.c new file mode 100644 index 0000000..1051eb0 --- /dev/null +++ b/bn_mp_invmod.c @@ -0,0 +1,203 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +int +mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, A, B, C, D; + int res; + + + /* b cannot be negative */ + if (b->sign == MP_NEG) { + return MP_VAL; + } + + /* if the modulus is odd we can use a faster routine instead */ + if (mp_iseven (b) == 0) { + res = fast_mp_invmod (a, b, c); + return res; + } + + if ((res = mp_init (&x)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_init (&y)) != MP_OKAY) { + goto __X; + } + + if ((res = mp_init (&u)) != MP_OKAY) { + goto __Y; + } + + if ((res = mp_init (&v)) != MP_OKAY) { + goto __U; + } + + if ((res = mp_init (&A)) != MP_OKAY) { + goto __V; + } + + if ((res = mp_init (&B)) != MP_OKAY) { + goto __A; + } + + if ((res = mp_init (&C)) != MP_OKAY) { + goto __B; + } + + if ((res = mp_init (&D)) != MP_OKAY) { + goto __C; + } + + /* x = a, y = b */ + if ((res = mp_copy (a, &x)) != MP_OKAY) { + goto __D; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_abs (&x, &x)) != MP_OKAY) { + goto __D; + } + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { + res = MP_VAL; + goto __D; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto __D; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto __D; + } + mp_set (&A, 1); + mp_set (&D, 1); + + +top: + /* 4. while u is even do */ + while (mp_iseven (&u) == 1) { + /* 4.1 u = u/2 */ + if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { + goto __D; + } + /* 4.2 if A or B is odd then */ + if (mp_iseven (&A) == 0 || mp_iseven (&B) == 0) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto __D; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto __D; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto __D; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto __D; + } + } + + + /* 5. while v is even do */ + while (mp_iseven (&v) == 1) { + /* 5.1 v = v/2 */ + if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { + goto __D; + } + /* 5.2 if C,D are even then */ + if (mp_iseven (&C) == 0 || mp_iseven (&D) == 0) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto __D; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto __D; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto __D; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto __D; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto __D; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto __D; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto __D; + } + } + + /* if not zero goto step 4 */ + if (mp_iszero (&u) == 0) + goto top; + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d (&v, 1) != MP_EQ) { + res = MP_VAL; + goto __D; + } + + /* a is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; + +__D:mp_clear (&D); +__C:mp_clear (&C); +__B:mp_clear (&B); +__A:mp_clear (&A); +__V:mp_clear (&v); +__U:mp_clear (&u); +__Y:mp_clear (&y); +__X:mp_clear (&x); +__ERR: + return res; +} diff --git a/bn_mp_jacobi.c b/bn_mp_jacobi.c new file mode 100644 index 0000000..b97d5f3 --- /dev/null +++ b/bn_mp_jacobi.c @@ -0,0 +1,114 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* computes the jacobi c = (a | n) (or Legendre if b is prime) + * HAC pp. 73 Algorithm 2.149 + */ +int +mp_jacobi (mp_int * a, mp_int * n, int *c) +{ + mp_int a1, n1, e; + int s, r, res; + mp_digit residue; + + /* step 1. if a == 0, return 0 */ + if (mp_iszero (a) == 1) { + *c = 0; + return MP_OKAY; + } + + /* step 2. if a == 1, return 1 */ + if (mp_cmp_d (a, 1) == MP_EQ) { + *c = 1; + return MP_OKAY; + } + + /* default */ + s = 0; + + /* step 3. write a = a1 * 2^e */ + if ((res = mp_init_copy (&a1, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&n1)) != MP_OKAY) { + goto __A1; + } + + if ((res = mp_init (&e)) != MP_OKAY) { + goto __N1; + } + + while (mp_iseven (&a1) == 1) { + if ((res = mp_add_d (&e, 1, &e)) != MP_OKAY) { + goto __E; + } + + if ((res = mp_div_2 (&a1, &a1)) != MP_OKAY) { + goto __E; + } + } + + /* step 4. if e is even set s=1 */ + if (mp_iseven (&e) == 1) { + s = 1; + } else { + /* else set s=1 if n = 1/7 (mod 8) or s=-1 if n = 3/5 (mod 8) */ + if ((res = mp_mod_d (n, 8, &residue)) != MP_OKAY) { + goto __E; + } + + if (residue == 1 || residue == 7) { + s = 1; + } else if (residue == 3 || residue == 5) { + s = -1; + } + } + + /* step 5. if n == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ + if ((res = mp_mod_d (n, 4, &residue)) != MP_OKAY) { + goto __E; + } + if (residue == 3) { + if ((res = mp_mod_d (&a1, 4, &residue)) != MP_OKAY) { + goto __E; + } + if (residue == 3) { + s = -s; + } + } + + /* if a1 == 1 we're done */ + if (mp_cmp_d (&a1, 1) == MP_EQ) { + *c = s; + } else { + /* n1 = n mod a1 */ + if ((res = mp_mod (n, &a1, &n1)) != MP_OKAY) { + goto __E; + } + if ((res = mp_jacobi (&n1, &a1, &r)) != MP_OKAY) { + goto __E; + } + *c = s * r; + } + + /* done */ + res = MP_OKAY; +__E:mp_clear (&e); +__N1:mp_clear (&n1); +__A1:mp_clear (&a1); + return res; +} diff --git a/bn_mp_karatsuba_mul.c b/bn_mp_karatsuba_mul.c new file mode 100644 index 0000000..0a8c34e --- /dev/null +++ b/bn_mp_karatsuba_mul.c @@ -0,0 +1,142 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* c = |a| * |b| using Karatsuba Multiplication using three half size multiplications + * + * Let B represent the radix [e.g. 2**DIGIT_BIT] and let n represent half of the number of digits in the min(a,b) + * + * a = a1 * B^n + a0 + * b = b1 * B^n + b0 + * + * Then, a * b => a1b1 * B^2n + ((a1 - b1)(a0 - b0) + a0b0 + a1b1) * B + a0b0 + * + * Note that a1b1 and a0b0 are used twice and only need to be computed once. So in total + * three half size (half # of digit) multiplications are performed, a0b0, a1b1 and (a1-b1)(a0-b0) + * + * Note that a multiplication of half the digits requires 1/4th the number of single precision + * multiplications so in total after one call 25% of the single precision multiplications are saved. + * Note also that the call to mp_mul can end up back in this function if the a0, a1, b0, or b1 are above + * the threshold. This is known as divide-and-conquer and leads to the famous O(N^lg(3)) or O(N^1.584) work which + * is asymptopically lower than the standard O(N^2) that the baseline/comba methods use. Generally though the + * overhead of this method doesn't pay off until a certain size (N ~ 80) is reached. + */ +int +mp_karatsuba_mul (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x0, x1, y0, y1, t1, t2, x0y0, x1y1; + int B, err, x; + + + err = MP_MEM; + + /* min # of digits */ + B = MIN (a->used, b->used); + + /* now divide in two */ + B = B / 2; + + /* init copy all the temps */ + if (mp_init_size (&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size (&x1, a->used - B) != MP_OKAY) + goto X0; + if (mp_init_size (&y0, B) != MP_OKAY) + goto X1; + if (mp_init_size (&y1, b->used - B) != MP_OKAY) + goto Y0; + + /* init temps */ + if (mp_init (&t1) != MP_OKAY) + goto Y1; + if (mp_init (&t2) != MP_OKAY) + goto T1; + if (mp_init (&x0y0) != MP_OKAY) + goto T2; + if (mp_init (&x1y1) != MP_OKAY) + goto X0Y0; + + /* now shift the digits */ + x0.sign = x1.sign = a->sign; + y0.sign = y1.sign = b->sign; + + x0.used = y0.used = B; + x1.used = a->used - B; + y1.used = b->used - B; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + for (x = 0; x < B; x++) { + x0.dp[x] = a->dp[x]; + y0.dp[x] = b->dp[x]; + } + for (x = B; x < a->used; x++) { + x1.dp[x - B] = a->dp[x]; + } + for (x = B; x < b->used; x++) { + y1.dp[x - B] = b->dp[x]; + } + + /* only need to clamp the lower words since by definition the upper words x1/y1 must + * have a known number of digits + */ + mp_clamp (&x0); + mp_clamp (&y0); + + /* now calc the products x0y0 and x1y1 */ + if (mp_mul (&x0, &y0, &x0y0) != MP_OKAY) + goto X1Y1; /* x0y0 = x0*y0 */ + if (mp_mul (&x1, &y1, &x1y1) != MP_OKAY) + goto X1Y1; /* x1y1 = x1*y1 */ + + /* now calc x1-x0 and y1-y0 */ + if (mp_sub (&x1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = x1 - x0 */ + if (mp_sub (&y1, &y0, &t2) != MP_OKAY) + goto X1Y1; /* t2 = y1 - y0 */ + if (mp_mul (&t1, &t2, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1 - x0) * (y1 - y0) */ + + /* add x0y0 */ + if (mp_add (&x0y0, &x1y1, &t2) != MP_OKAY) + goto X1Y1; /* t2 = x0y0 + x1y1 */ + if (mp_sub (&t2, &t1, &t1) != MP_OKAY) + goto X1Y1; /* t1 = x0y0 + x1y1 - (x1-x0)*(y1-y0) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))< + +/* Karatsuba squaring, computes b = a*a using three half size squarings + * + * See comments of mp_karatsuba_mul for details. It is essentially the same algorithm + * but merely tuned to perform recursive squarings. + */ +int +mp_karatsuba_sqr (mp_int * a, mp_int * b) +{ + mp_int x0, x1, t1, t2, x0x0, x1x1; + int B, err, x; + + + err = MP_MEM; + + /* min # of digits */ + B = a->used; + + /* now divide in two */ + B = B / 2; + + /* init copy all the temps */ + if (mp_init_size (&x0, B) != MP_OKAY) + goto ERR; + if (mp_init_size (&x1, a->used - B) != MP_OKAY) + goto X0; + + /* init temps */ + if (mp_init (&t1) != MP_OKAY) + goto X1; + if (mp_init (&t2) != MP_OKAY) + goto T1; + if (mp_init (&x0x0) != MP_OKAY) + goto T2; + if (mp_init (&x1x1) != MP_OKAY) + goto X0X0; + + /* now shift the digits */ + for (x = 0; x < B; x++) { + x0.dp[x] = a->dp[x]; + } + + for (x = B; x < a->used; x++) { + x1.dp[x - B] = a->dp[x]; + } + + x0.used = B; + x1.used = a->used - B; + + mp_clamp (&x0); + + /* 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 */ + + /* 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) */ + + /* add x0y0 */ + if (mp_add (&x0x0, &x1x1, &t2) != MP_OKAY) + goto X1X1; /* t2 = x0y0 + x1y1 */ + if (mp_sub (&t2, &t1, &t1) != MP_OKAY) + goto X1X1; /* t1 = x0y0 + x1y1 - (x1-x0)*(y1-y0) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1X1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))< + +/* computes least common multiple as a*b/(a, b) */ +int +mp_lcm (mp_int * a, mp_int * b, mp_int * c) +{ + int res; + mp_int t; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if ((res = mp_gcd (a, b, c)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + res = mp_div (&t, c, c, NULL); + mp_clear (&t); + return res; +} diff --git a/bn_mp_lshd.c b/bn_mp_lshd.c new file mode 100644 index 0000000..ea02409 --- /dev/null +++ b/bn_mp_lshd.c @@ -0,0 +1,46 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* shift left a certain amount of digits */ +int +mp_lshd (mp_int * a, int b) +{ + int x, res; + + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + + /* increment the used by the shift amount than copy upwards */ + a->used += b; + for (x = a->used - 1; x >= b; x--) { + a->dp[x] = a->dp[x - b]; + } + + /* zero the lower digits */ + for (x = 0; x < b; x++) { + a->dp[x] = 0; + } + mp_clamp (a); + return MP_OKAY; +} diff --git a/bn_mp_mod.c b/bn_mp_mod.c new file mode 100644 index 0000000..5b208a6 --- /dev/null +++ b/bn_mp_mod.c @@ -0,0 +1,43 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* c = a mod b, 0 <= c < b */ +int +mp_mod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int t; + int res; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + if (t.sign == MP_NEG) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} diff --git a/bn_mp_mod_2d.c b/bn_mp_mod_2d.c new file mode 100644 index 0000000..df73612 --- /dev/null +++ b/bn_mp_mod_2d.c @@ -0,0 +1,51 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* calc a value mod 2^b */ +int +mp_mod_2d (mp_int * a, int b, mp_int * c) +{ + int x, res; + + + /* if b is <= 0 then zero the int */ + if (b <= 0) { + mp_zero (c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b > (int) (a->used * DIGIT_BIT)) { + res = mp_copy (a, c); + return res; + } + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + /* zero digits above the last digit of the modulus */ + for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { + c->dp[x] = 0; + } + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / DIGIT_BIT] &= + (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - + ((mp_digit) 1)); + mp_clamp (c); + return MP_OKAY; +} diff --git a/bn_mp_mod_d.c b/bn_mp_mod_d.c new file mode 100644 index 0000000..7b08d23 --- /dev/null +++ b/bn_mp_mod_d.c @@ -0,0 +1,47 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +int +mp_mod_d (mp_int * a, mp_digit b, mp_digit * c) +{ + mp_int t, t2; + int res; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + mp_clear (&t); + return res; + } + + mp_set (&t, b); + mp_div (a, &t, NULL, &t2); + + if (t2.sign == MP_NEG) { + if ((res = mp_add_d (&t2, b, &t2)) != MP_OKAY) { + mp_clear (&t); + mp_clear (&t2); + return res; + } + } + *c = t2.dp[0]; + mp_clear (&t); + mp_clear (&t2); + return MP_OKAY; +} diff --git a/bn_mp_montgomery_reduce.c b/bn_mp_montgomery_reduce.c new file mode 100644 index 0000000..aeb2cde --- /dev/null +++ b/bn_mp_montgomery_reduce.c @@ -0,0 +1,80 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* computes xR^-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce (mp_int * a, mp_int * m, mp_digit mp) +{ + int ix, res, digs; + mp_digit ui; + + digs = m->used * 2 + 1; + if ((digs < 512) + && digs < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_mp_montgomery_reduce (a, m, mp); + return res; + } + + if (a->alloc < m->used * 2 + 1) { + if ((res = mp_grow (a, m->used * 2 + 1)) != MP_OKAY) { + return res; + } + } + a->used = m->used * 2 + 1; + + for (ix = 0; ix < m->used; ix++) { + /* ui = ai * m' mod b */ + ui = (a->dp[ix] * mp) & MP_MASK; + + /* a = a + ui * m * b^i */ + { + register int iy; + register mp_digit *tmpx, *tmpy, mu; + register mp_word r; + + /* aliases */ + tmpx = m->dp; + tmpy = a->dp + ix; + + mu = 0; + for (iy = 0; iy < m->used; iy++) { + r = + ((mp_word) ui) * ((mp_word) * tmpx++) + ((mp_word) mu) + + ((mp_word) * tmpy); + mu = (r >> ((mp_word) DIGIT_BIT)); + *tmpy++ = (r & ((mp_word) MP_MASK)); + } + /* propagate carries */ + while (mu) { + *tmpy += mu; + mu = (*tmpy >> DIGIT_BIT) & 1; + *tmpy++ &= MP_MASK; + } + } + } + + /* A = A/b^n */ + mp_rshd (a, m->used); + + /* 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) { + return res; + } + } + + return MP_OKAY; +} diff --git a/bn_mp_montgomery_setup.c b/bn_mp_montgomery_setup.c new file mode 100644 index 0000000..601e74c --- /dev/null +++ b/bn_mp_montgomery_setup.c @@ -0,0 +1,53 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup (mp_int * a, mp_digit * mp) +{ + mp_int t, tt; + int res; + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&tt)) != MP_OKAY) { + goto __T; + } + + /* tt = b */ + tt.dp[0] = 0; + tt.dp[1] = 1; + tt.used = 2; + + /* t = m mod b */ + t.dp[0] = a->dp[0]; + t.used = 1; + + /* t = 1/m mod b */ + if ((res = mp_invmod (&t, &tt, &t)) != MP_OKAY) { + goto __TT; + } + + /* t = -1/m mod b */ + *mp = ((mp_digit) 1 << ((mp_digit) DIGIT_BIT)) - t.dp[0]; + + res = MP_OKAY; +__TT:mp_clear (&tt); +__T:mp_clear (&t); + return res; +} diff --git a/bn_mp_mul.c b/bn_mp_mul.c new file mode 100644 index 0000000..cfa1467 --- /dev/null +++ b/bn_mp_mul.c @@ -0,0 +1,30 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* high level multiplication (handles sign) */ +int +mp_mul (mp_int * a, mp_int * b, mp_int * c) +{ + int res, neg; + neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; + if (MIN (a->used, b->used) > KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else { + res = s_mp_mul (a, b, c); + } + c->sign = neg; + return res; +} diff --git a/bn_mp_mul_2.c b/bn_mp_mul_2.c new file mode 100644 index 0000000..dd5ecca --- /dev/null +++ b/bn_mp_mul_2.c @@ -0,0 +1,44 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* b = a*2 */ +int +mp_mul_2 (mp_int * a, mp_int * b) +{ + mp_digit r, rr; + int x, res; + + + /* copy */ + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + + if ((res = mp_grow (b, b->used + 1)) != MP_OKAY) { + return res; + } + ++b->used; + + /* shift any bit count < DIGIT_BIT */ + r = 0; + for (x = 0; x < b->used; x++) { + rr = (b->dp[x] >> (DIGIT_BIT - 1)) & 1; + b->dp[x] = ((b->dp[x] << 1) | r) & MP_MASK; + r = rr; + } + mp_clamp (b); + return MP_OKAY; +} diff --git a/bn_mp_mul_2d.c b/bn_mp_mul_2d.c new file mode 100644 index 0000000..7823eb9 --- /dev/null +++ b/bn_mp_mul_2d.c @@ -0,0 +1,57 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* shift left by a certain bit count */ +int +mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d, r, rr; + int x, res; + + + /* copy */ + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + + if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* shift by as many digits in the bit count */ + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + c->used = c->alloc; + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (c->dp[x] >> (DIGIT_BIT - d)) & ((mp_digit) ((1U << d) - 1U)); + + /* shift the current word and OR in the carry */ + c->dp[x] = ((c->dp[x] << d) | r) & MP_MASK; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + } + mp_clamp (c); + return MP_OKAY; +} diff --git a/bn_mp_mul_d.c b/bn_mp_mul_d.c new file mode 100644 index 0000000..bced9b7 --- /dev/null +++ b/bn_mp_mul_d.c @@ -0,0 +1,46 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* multiply by a digit */ +int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, pa, ix; + mp_word r; + mp_digit u; + mp_int t; + + + pa = a->used; + if ((res = mp_init_size (&t, pa + 2)) != MP_OKAY) { + return res; + } + t.used = pa + 2; + + u = 0; + for (ix = 0; ix < pa; ix++) { + r = ((mp_word) u) + ((mp_word) a->dp[ix]) * ((mp_word) b); + t.dp[ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + t.dp[ix] = u; + + t.sign = a->sign; + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_mp_mulmod.c b/bn_mp_mulmod.c new file mode 100644 index 0000000..2cdbdda --- /dev/null +++ b/bn_mp_mulmod.c @@ -0,0 +1,36 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* d = a * b (mod c) */ +int +mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_mul (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} diff --git a/bn_mp_n_root.c b/bn_mp_n_root.c new file mode 100644 index 0000000..72b3a8c --- /dev/null +++ b/bn_mp_n_root.c @@ -0,0 +1,115 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* find the n'th root of an integer + * + * Result found such that (c)^b <= a and (c+1)^b > a + */ +int +mp_n_root (mp_int * a, mp_digit b, mp_int * c) +{ + mp_int t1, t2, t3; + int res, neg; + + /* input must be positive if b is even */ + if ((b & 1) == 0 && a->sign == MP_NEG) { + return MP_VAL; + } + + if ((res = mp_init (&t1)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&t2)) != MP_OKAY) { + goto __T1; + } + + if ((res = mp_init (&t3)) != MP_OKAY) { + goto __T2; + } + + /* if a is negative fudge the sign but keep track */ + neg = a->sign; + a->sign = MP_ZPOS; + + /* t2 = 2 */ + mp_set (&t2, 2); + + do { + /* t1 = t2 */ + if ((res = mp_copy (&t2, &t1)) != MP_OKAY) { + goto __T3; + } + + /* t2 = t1 - ((t1^b - a) / (b * t1^(b-1))) */ + if ((res = mp_expt_d (&t1, b - 1, &t3)) != MP_OKAY) { /* t3 = t1^(b-1) */ + goto __T3; + } + + /* numerator */ + if ((res = mp_mul (&t3, &t1, &t2)) != MP_OKAY) { /* t2 = t1^b */ + goto __T3; + } + + if ((res = mp_sub (&t2, a, &t2)) != MP_OKAY) { /* t2 = t1^b - a */ + goto __T3; + } + + if ((res = mp_mul_d (&t3, b, &t3)) != MP_OKAY) { /* t3 = t1^(b-1) * b */ + goto __T3; + } + + if ((res = mp_div (&t2, &t3, &t3, NULL)) != MP_OKAY) { /* t3 = (t1^b - a)/(b * t1^(b-1)) */ + goto __T3; + } + + if ((res = mp_sub (&t1, &t3, &t2)) != MP_OKAY) { + goto __T3; + } + } + while (mp_cmp (&t1, &t2) != MP_EQ); + + /* result can be off by a few so check */ + for (;;) { + if ((res = mp_expt_d (&t1, b, &t2)) != MP_OKAY) { + goto __T3; + } + + if (mp_cmp (&t2, a) == MP_GT) { + if ((res = mp_sub_d (&t1, 1, &t1)) != MP_OKAY) { + goto __T3; + } + } else { + break; + } + } + + /* reset the sign of a first */ + a->sign = neg; + + /* set the result */ + mp_exch (&t1, c); + + /* set the sign of the result */ + c->sign = neg; + + res = MP_OKAY; + +__T3:mp_clear (&t3); +__T2:mp_clear (&t2); +__T1:mp_clear (&t1); + return res; +} diff --git a/bn_mp_neg.c b/bn_mp_neg.c new file mode 100644 index 0000000..a2bb7c4 --- /dev/null +++ b/bn_mp_neg.c @@ -0,0 +1,27 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* b = -a */ +int +mp_neg (mp_int * a, mp_int * b) +{ + int res; + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; + return MP_OKAY; +} diff --git a/bn_mp_or.c b/bn_mp_or.c new file mode 100644 index 0000000..a3843d9 --- /dev/null +++ b/bn_mp_or.c @@ -0,0 +1,45 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* OR two ints together */ +int +mp_or (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] |= x->dp[ix]; + } + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_mp_rand.c b/bn_mp_rand.c new file mode 100644 index 0000000..c72dec9 --- /dev/null +++ b/bn_mp_rand.c @@ -0,0 +1,48 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* makes a pseudo-random int of a given size */ +int +mp_rand (mp_int * a, int digits) +{ + int res; + mp_digit d; + + mp_zero (a); + if (digits <= 0) { + return MP_OKAY; + } + + /* first place a random non-zero digit */ + d = ((mp_digit) abs (rand ())); + d = d == 0 ? 1 : d; + + if ((res = mp_add_d (a, d, a)) != MP_OKAY) { + return res; + } + + + while (digits-- > 0) { + if ((res = mp_add_d (a, ((mp_digit) abs (rand ())), a)) != MP_OKAY) { + return res; + } + if ((res = mp_lshd (a, 1)) != MP_OKAY) { + return res; + } + } + + return MP_OKAY; +} diff --git a/bn_mp_read_signed_bin.c b/bn_mp_read_signed_bin.c new file mode 100644 index 0000000..b5af4c0 --- /dev/null +++ b/bn_mp_read_signed_bin.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* read signed bin, big endian, first byte is 0==positive or 1==negative */ +int +mp_read_signed_bin (mp_int * a, unsigned char *b, int c) +{ + int res; + + if ((res = mp_read_unsigned_bin (a, b + 1, c - 1)) != MP_OKAY) { + return res; + } + a->sign = ((b[0] == (unsigned char) 0) ? MP_ZPOS : MP_NEG); + return MP_OKAY; +} diff --git a/bn_mp_read_unsigned_bin.c b/bn_mp_read_unsigned_bin.c new file mode 100644 index 0000000..726b574 --- /dev/null +++ b/bn_mp_read_unsigned_bin.c @@ -0,0 +1,39 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* reads a unsigned char array, assumes the msb is stored first [big endian] */ +int +mp_read_unsigned_bin (mp_int * a, unsigned char *b, int c) +{ + int res; + mp_zero (a); + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + + if (DIGIT_BIT != 7) { + a->dp[0] |= *b++; + a->used += 1; + } else { + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; + } + } + mp_clamp (a); + return MP_OKAY; +} diff --git a/bn_mp_reduce.c b/bn_mp_reduce.c new file mode 100644 index 0000000..be5d18e --- /dev/null +++ b/bn_mp_reduce.c @@ -0,0 +1,95 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +int +mp_reduce_setup (mp_int * a, mp_int * b) +{ + int res; + + + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { + return res; + } + res = mp_div (a, b, a, NULL); + return res; +} + +/* reduces x mod m, assumes 0 < x < m^2, mu is precomputed via mp_reduce_setup + * From HAC pp.604 Algorithm 14.42 + */ +int +mp_reduce (mp_int * x, mp_int * m, mp_int * mu) +{ + mp_int q; + int res, um = m->used; + + + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + mp_rshd (&q, um - 1); /* q1 = x / b^(k-1) */ + + /* according to HAC this is optimization is ok */ + if (((unsigned long) m->used) > (1UL << (unsigned long) (DIGIT_BIT - 1UL))) { + if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { + goto CLEANUP; + } + } else { + if ((res = s_mp_mul_high_digs (&q, mu, &q, um - 1)) != MP_OKAY) { + goto CLEANUP; + } + } + + mp_rshd (&q, um + 1); /* q3 = q2 / b^(k+1) */ + + /* x = x mod b^(k+1), quick (no division) */ + if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto CLEANUP; + } + + /* q = q * m mod b^(k+1), quick (no division) */ + if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { + goto CLEANUP; + } + + /* x = x - q */ + if ((res = mp_sub (x, &q, x)) != MP_OKAY) + goto CLEANUP; + + /* If x < 0, add b^(k+1) to it */ + if (mp_cmp_d (x, 0) == MP_LT) { + mp_set (&q, 1); + if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) + goto CLEANUP; + if ((res = mp_add (x, &q, x)) != MP_OKAY) + goto CLEANUP; + } + + /* Back off if it's too big */ + while (mp_cmp (x, m) != MP_LT) { + if ((res = s_mp_sub (x, m, x)) != MP_OKAY) + break; + } + +CLEANUP: + mp_clear (&q); + + return res; +} diff --git a/bn_mp_rshd.c b/bn_mp_rshd.c new file mode 100644 index 0000000..8b37d09 --- /dev/null +++ b/bn_mp_rshd.c @@ -0,0 +1,45 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* shift right a certain amount of digits */ +void +mp_rshd (mp_int * a, int b) +{ + int x; + + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used < b) { + mp_zero (a); + return; + } + + /* shift the digits down */ + for (x = 0; x < (a->used - b); x++) { + a->dp[x] = a->dp[x + b]; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + a->dp[x] = 0; + } + mp_clamp (a); +} diff --git a/bn_mp_set.c b/bn_mp_set.c new file mode 100644 index 0000000..aeaf9cd --- /dev/null +++ b/bn_mp_set.c @@ -0,0 +1,24 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* set to a digit */ +void +mp_set (mp_int * a, mp_digit b) +{ + mp_zero (a); + a->dp[0] = b & MP_MASK; + a->used = (a->dp[0] != 0) ? 1 : 0; +} diff --git a/bn_mp_set_int.c b/bn_mp_set_int.c new file mode 100644 index 0000000..a690bb4 --- /dev/null +++ b/bn_mp_set_int.c @@ -0,0 +1,45 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* set a 32-bit const */ +int +mp_set_int (mp_int * a, unsigned long b) +{ + int x, res; + + mp_zero (a); + + /* set four bits at a time, simplest solution to the what if DIGIT_BIT==7 case */ + for (x = 0; x < 8; x++) { + + /* shift the number up four bits */ + if ((res = mp_mul_2d (a, 4, a)) != MP_OKAY) { + return res; + } + + /* OR in the top four bits of the source */ + a->dp[0] |= (b >> 28) & 15; + + /* shift the source up to the next four bits */ + b <<= 4; + + /* ensure that digits are not clamped off */ + a->used += 32 / DIGIT_BIT + 1; + } + + mp_clamp (a); + return MP_OKAY; +} diff --git a/bn_mp_shrink.c b/bn_mp_shrink.c new file mode 100644 index 0000000..3cc506b --- /dev/null +++ b/bn_mp_shrink.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* shrink a bignum */ +int +mp_shrink (mp_int * a) +{ + if (a->alloc != a->used) { + if ((a->dp = realloc (a->dp, sizeof (mp_digit) * a->used)) == NULL) { + return MP_MEM; + } + a->alloc = a->used; + } + return MP_OKAY; +} diff --git a/bn_mp_signed_bin_size.c b/bn_mp_signed_bin_size.c new file mode 100644 index 0000000..d50ae7a --- /dev/null +++ b/bn_mp_signed_bin_size.c @@ -0,0 +1,22 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* get the size for an signed equivalent */ +int +mp_signed_bin_size (mp_int * a) +{ + return 1 + mp_unsigned_bin_size (a); +} diff --git a/bn_mp_sqr.c b/bn_mp_sqr.c new file mode 100644 index 0000000..2ba877c --- /dev/null +++ b/bn_mp_sqr.c @@ -0,0 +1,29 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* computes b = a*a */ +int +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + if (a->used > KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else { + res = s_mp_sqr (a, b); + } + b->sign = MP_ZPOS; + return res; +} diff --git a/bn_mp_sqrmod.c b/bn_mp_sqrmod.c new file mode 100644 index 0000000..66135d4 --- /dev/null +++ b/bn_mp_sqrmod.c @@ -0,0 +1,36 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* c = a * a (mod b) */ +int +mp_sqrmod (mp_int * a, mp_int * b, mp_int * c) +{ + int res; + mp_int t; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sqr (a, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, b, c); + mp_clear (&t); + return res; +} diff --git a/bn_mp_sub.c b/bn_mp_sub.c new file mode 100644 index 0000000..045dee5 --- /dev/null +++ b/bn_mp_sub.c @@ -0,0 +1,58 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* high level subtraction (handles signs) */ +int +mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + + sa = a->sign; + sb = b->sign; + + /* handle four cases */ + if (sa == MP_ZPOS && sb == MP_ZPOS) { + /* both positive, a - b, but if b>a then we do -(b - a) */ + if (mp_cmp_mag (a, b) == MP_LT) { + /* b>a */ + res = s_mp_sub (b, a, c); + c->sign = MP_NEG; + } else { + res = s_mp_sub (a, b, c); + c->sign = MP_ZPOS; + } + } else if (sa == MP_ZPOS && sb == MP_NEG) { + /* a - -b == a + b */ + res = s_mp_add (a, b, c); + c->sign = MP_ZPOS; + } else if (sa == MP_NEG && sb == MP_ZPOS) { + /* -a - b == -(a + b) */ + res = s_mp_add (a, b, c); + c->sign = MP_NEG; + } else { + /* -a - -b == b - a, but if a>b == -(a - b) */ + if (mp_cmp_mag (a, b) == MP_GT) { + res = s_mp_sub (a, b, c); + c->sign = MP_NEG; + } else { + res = s_mp_sub (b, a, c); + c->sign = MP_ZPOS; + } + } + + return res; +} diff --git a/bn_mp_sub_d.c b/bn_mp_sub_d.c new file mode 100644 index 0000000..9839d5e --- /dev/null +++ b/bn_mp_sub_d.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* single digit subtraction */ +int +mp_sub_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_int t; + int res; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + mp_set (&t, b); + res = mp_sub (a, &t, c); + + mp_clear (&t); + return res; +} diff --git a/bn_mp_submod.c b/bn_mp_submod.c new file mode 100644 index 0000000..b56d921 --- /dev/null +++ b/bn_mp_submod.c @@ -0,0 +1,36 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* d = a - b (mod c) */ +int +mp_submod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) +{ + int res; + mp_int t; + + + if ((res = mp_init (&t)) != MP_OKAY) { + return res; + } + + if ((res = mp_sub (a, b, &t)) != MP_OKAY) { + mp_clear (&t); + return res; + } + res = mp_mod (&t, c, d); + mp_clear (&t); + return res; +} diff --git a/bn_mp_to_signed_bin.c b/bn_mp_to_signed_bin.c new file mode 100644 index 0000000..b00cf8f --- /dev/null +++ b/bn_mp_to_signed_bin.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* store in signed [big endian] format */ +int +mp_to_signed_bin (mp_int * a, unsigned char *b) +{ + int res; + + if ((res = mp_to_unsigned_bin (a, b + 1)) != MP_OKAY) { + return res; + } + b[0] = (unsigned char) ((a->sign == MP_ZPOS) ? 0 : 1); + return MP_OKAY; +} diff --git a/bn_mp_to_unsigned_bin.c b/bn_mp_to_unsigned_bin.c new file mode 100644 index 0000000..b122555 --- /dev/null +++ b/bn_mp_to_unsigned_bin.c @@ -0,0 +1,43 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* store in unsigned [big endian] format */ +int +mp_to_unsigned_bin (mp_int * a, unsigned char *b) +{ + int x, res; + mp_int t; + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + x = 0; + while (mp_iszero (&t) == 0) { + if (DIGIT_BIT != 7) { + b[x++] = (unsigned char) (t.dp[0] & 255); + } else { + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); + } + if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { + mp_clear (&t); + return res; + } + } + bn_reverse (b, x); + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_mp_unsigned_bin_size.c b/bn_mp_unsigned_bin_size.c new file mode 100644 index 0000000..1c92b5c --- /dev/null +++ b/bn_mp_unsigned_bin_size.c @@ -0,0 +1,23 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* get the size for an unsigned equivalent */ +int +mp_unsigned_bin_size (mp_int * a) +{ + int size = mp_count_bits (a); + return (size / 8 + ((size & 7) != 0 ? 1 : 0)); +} diff --git a/bn_mp_xor.c b/bn_mp_xor.c new file mode 100644 index 0000000..c8e9f43 --- /dev/null +++ b/bn_mp_xor.c @@ -0,0 +1,45 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* XOR two ints together */ +int +mp_xor (mp_int * a, mp_int * b, mp_int * c) +{ + int res, ix, px; + mp_int t, *x; + + if (a->used > b->used) { + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + px = b->used; + x = b; + } else { + if ((res = mp_init_copy (&t, b)) != MP_OKAY) { + return res; + } + px = a->used; + x = a; + } + + for (ix = 0; ix < px; ix++) { + t.dp[ix] ^= x->dp[ix]; + } + mp_clamp (&t); + mp_exch (c, &t); + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_mp_zero.c b/bn_mp_zero.c new file mode 100644 index 0000000..27ca5bd --- /dev/null +++ b/bn_mp_zero.c @@ -0,0 +1,24 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* set to zero */ +void +mp_zero (mp_int * a) +{ + a->sign = MP_ZPOS; + a->used = 0; + memset (a->dp, 0, sizeof (mp_digit) * a->alloc); +} diff --git a/bn_radix.c b/bn_radix.c new file mode 100644 index 0000000..1fc4b35 --- /dev/null +++ b/bn_radix.c @@ -0,0 +1,139 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* chars used in radix conversions */ +static const char *s_rmap = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; + + +/* read a string [ASCII] in a given radix */ +int +mp_read_radix (mp_int * a, char *str, int radix) +{ + int y, res, neg; + char ch; + + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + if (*str == '-') { + ++str; + neg = MP_NEG; + } else { + neg = MP_ZPOS; + } + + mp_zero (a); + while (*str) { + ch = (char) ((radix < 36) ? toupper (*str) : *str); + for (y = 0; y < 64; y++) { + if (ch == s_rmap[y]) { + break; + } + } + + if (y < radix) { + if ((res = mp_mul_d (a, (mp_digit) radix, a)) != MP_OKAY) { + return res; + } + if ((res = mp_add_d (a, (mp_digit) y, a)) != MP_OKAY) { + return res; + } + } else { + break; + } + ++str; + } + a->sign = neg; + return MP_OKAY; +} + +/* stores a bignum as a ASCII string in a given radix (2..64) */ +int +mp_toradix (mp_int * a, char *str, int radix) +{ + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + if (t.sign == MP_NEG) { + ++_s; + *str++ = '-'; + t.sign = MP_ZPOS; + } + + digs = 0; + while (mp_iszero (&t) == 0) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + *str++ = s_rmap[d]; + ++digs; + } + bn_reverse ((unsigned char *) _s, digs); + *str++ = '\0'; + mp_clear (&t); + return MP_OKAY; +} + +/* returns size of ASCII reprensentation */ +int +mp_radix_size (mp_int * a, int radix) +{ + int res, digs; + mp_int t; + mp_digit d; + + /* special case for binary */ + if (radix == 2) { + return mp_count_bits (a) + (a->sign == MP_NEG ? 1 : 0) + 1; + } + + if (radix < 2 || radix > 64) { + return 0; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return 0; + } + + digs = 0; + if (t.sign == MP_NEG) { + ++digs; + t.sign = MP_ZPOS; + } + + while (mp_iszero (&t) == 0) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return 0; + } + ++digs; + } + mp_clear (&t); + return digs + 1; +} diff --git a/bn_reverse.c b/bn_reverse.c new file mode 100644 index 0000000..10c2375 --- /dev/null +++ b/bn_reverse.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* reverse an array, used for radix code */ +void +bn_reverse (unsigned char *s, int len) +{ + int ix, iy; + unsigned char t; + + ix = 0; + iy = len - 1; + while (ix < iy) { + t = s[ix]; + s[ix] = s[iy]; + s[iy] = t; + ++ix; + --iy; + } +} diff --git a/bn_s_mp_add.c b/bn_s_mp_add.c new file mode 100644 index 0000000..369a0e1 --- /dev/null +++ b/bn_s_mp_add.c @@ -0,0 +1,91 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +int +s_mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int *x; + int olduse, res, min, max, i; + mp_digit u; + + + /* find sizes, we let |a| <= |b| which means we have to sort + * them. "x" will point to the input with the most digits + */ + if (a->used > b->used) { + min = b->used; + max = a->used; + x = a; + } else if (a->used < b->used) { + min = a->used; + max = b->used; + x = b; + } else { + min = max = a->used; + x = NULL; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + olduse = c->used; + c->used = max + 1; + + /* add digits from lower part */ + + /* set the carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + c->dp[i] = a->dp[i] + b->dp[i] + u; + + /* U = carry bit of T[i] */ + u = (c->dp[i] >> DIGIT_BIT) & 1; + + /* take away carry bit from T[i] */ + c->dp[i] &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B if A or B has more digits add those in */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = X[i] + U */ + c->dp[i] = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = (c->dp[i] >> DIGIT_BIT) & 1; + + /* take away carry bit from T[i] */ + c->dp[i] &= MP_MASK; + } + } + + /* add carry */ + c->dp[i] = u; + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + c->dp[i] = 0; + } + + mp_clamp (c); + return MP_OKAY; +} diff --git a/bn_s_mp_mul_digs.c b/bn_s_mp_mul_digs.c new file mode 100644 index 0000000..55522a1 --- /dev/null +++ b/bn_s_mp_mul_digs.c @@ -0,0 +1,83 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* multiplies |a| * |b| and only computes upto digs digits of result + * HAC pp. 595, Algorithm 14.12 Modified so you can control how many digits of + * output are created. + */ +int +s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will have less than + * 512 digits and the number of digits won't affect carry propagation + */ + if ((digs < 512) + && digs < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + return res; + } + + if ((res = mp_init_size (&t, digs)) != MP_OKAY) { + return res; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + /* set the carry to zero */ + u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MIN (b->used, digs - ix); + + /* setup some aliases */ + tmpx = a->dp[ix]; + tmpt = &(t.dp[ix]); + tmpy = b->dp; + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + r = + ((mp_word) * tmpt) + ((mp_word) tmpx) * ((mp_word) * tmpy++) + + ((mp_word) u); + + /* the new column is the lower part of the result */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry word from the result */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + if (ix + iy < digs) + *tmpt = u; + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_s_mp_mul_high_digs.c b/bn_s_mp_mul_high_digs.c new file mode 100644 index 0000000..ff2530f --- /dev/null +++ b/bn_s_mp_mul_high_digs.c @@ -0,0 +1,77 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +int +s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) +{ + mp_int t; + int res, pa, pb, ix, iy; + mp_digit u; + mp_word r; + mp_digit tmpx, *tmpt, *tmpy; + + + /* can we use the fast multiplier? */ + if (((a->used + b->used + 1) < 512) + && MAX (a->used, + b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_high_digs (a, b, c, digs); + return res; + } + + if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { + return res; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + /* clear the carry */ + u = 0; + + /* left hand side of A[ix] * B[iy] */ + tmpx = a->dp[ix]; + + /* alias to the address of where the digits will be stored */ + tmpt = &(t.dp[digs]); + + /* alias for where to read the right hand side from */ + tmpy = b->dp + (digs - ix); + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + r = + ((mp_word) * tmpt) + ((mp_word) tmpx) * ((mp_word) * tmpy++) + + ((mp_word) u); + + /* get the lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* carry the carry */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + *tmpt = u; + } + mp_clamp (&t); + mp_exch (&t, c); + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_s_mp_sqr.c b/bn_s_mp_sqr.c new file mode 100644 index 0000000..94449a3 --- /dev/null +++ b/bn_s_mp_sqr.c @@ -0,0 +1,89 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +int +s_mp_sqr (mp_int * a, mp_int * b) +{ + mp_int t; + int res, ix, iy, pa; + mp_word r, u; + mp_digit tmpx, *tmpt; + + /* can we use the fast multiplier? */ + if (((a->used * 2 + 1) < 512) + && a->used < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT) - 1))) { + res = fast_s_mp_sqr (a, b); + return res; + } + + pa = a->used; + if ((res = mp_init_size (&t, pa + pa + 1)) != MP_OKAY) { + return res; + } + t.used = pa + pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = + ((mp_word) t.dp[ix + ix]) + + ((mp_word) a->dp[ix]) * ((mp_word) a->dp[ix]); + + /* store lower part in result */ + t.dp[ix + ix] = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get the carry */ + u = (r >> ((mp_word) DIGIT_BIT)); + + /* left hand side of A[ix] * A[iy] */ + tmpx = a->dp[ix]; + + /* alias for where to store the results */ + tmpt = &(t.dp[ix + ix + 1]); + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = ((mp_word) tmpx) * ((mp_word) a->dp[iy]); + + /* now calculate the double precision result, note we use + * addition instead of *2 since its easier to optimize + */ + r = ((mp_word) * tmpt) + r + r + ((mp_word) u); + + /* store lower part */ + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* get carry */ + u = (r >> ((mp_word) DIGIT_BIT)); + } + r = ((mp_word) * tmpt) + u; + *tmpt = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (r >> ((mp_word) DIGIT_BIT)); + /* propagate upwards */ + ++tmpt; + while (u != ((mp_word) 0)) { + r = ((mp_word) * tmpt) + ((mp_word) 1); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_s_mp_sub.c b/bn_s_mp_sub.c new file mode 100644 index 0000000..f6da162 --- /dev/null +++ b/bn_s_mp_sub.c @@ -0,0 +1,74 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +/* low level subtraction (assumes a > b), HAC pp.595 Algorithm 14.9 */ +int +s_mp_sub (mp_int * a, mp_int * b, mp_int * c) +{ + int olduse, res, min, max, i; + mp_digit u; + + + /* find sizes */ + min = b->used; + max = a->used; + + /* init result */ + if (c->alloc < max) { + if ((res = mp_grow (c, max)) != MP_OKAY) { + return res; + } + } + olduse = c->used; + c->used = max; + + /* sub digits from lower part */ + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + c->dp[i] = a->dp[i] - (b->dp[i] + u); + + /* U = carry bit of T[i] */ + u = (c->dp[i] >> DIGIT_BIT) & 1; + + /* Clear carry from T[i] */ + c->dp[i] &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = A[i] - U */ + c->dp[i] = a->dp[i] - u; + + /* U = carry bit of T[i] */ + u = (c->dp[i] >> DIGIT_BIT) & 1; + + /* Clear carry from T[i] */ + c->dp[i] &= MP_MASK; + } + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + c->dp[i] = 0; + } + + mp_clamp (c); + return MP_OKAY; +} diff --git a/bncore.c b/bncore.c new file mode 100644 index 0000000..5c7d098 --- /dev/null +++ b/bncore.c @@ -0,0 +1,19 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is library that provides for multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library is designed directly after the MPI library by + * Michael Fromberger but has been written from scratch with + * additional optimizations in place. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@iahu.ca, http://libtommath.iahu.ca + */ +#include + +int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */ + KARATSUBA_SQR_CUTOFF = 80, /* Min. number of digits before Karatsuba squaring is used. */ + MONTGOMERY_EXPT_CUTOFF = 40; /* max. number of digits that montgomery reductions will help for */ diff --git a/changes.txt b/changes.txt index e78a2a3..e2c9903 100644 --- a/changes.txt +++ b/changes.txt @@ -1,3 +1,13 @@ +Jan 17th, 2003 +v0.12 -- re-wrote the majority of the makefile so its more portable and will + install via "make install" on most *nix platforms + -- Re-packaged all the source as seperate files. Means the library a single + file packagage any more. Instead of just adding "bn.c" you have to add + libtommath.a + -- Renamed "bn.h" to "tommath.h" + -- Changes to the manual to reflect all of this + -- Used GNU Indent to clean up the source + Jan 15th, 2003 v0.11 -- More subtle fixes -- Moved to gentoo linux [hurrah!] so made *nix specific fixes to the make process diff --git a/demo.c b/demo/demo.c similarity index 94% rename from demo.c rename to demo/demo.c index f482120..3c3ef07 100644 --- a/demo.c +++ b/demo/demo.c @@ -14,24 +14,14 @@ typedef unsigned long long ulong64; #endif #else - #include "bn.h" -#endif - -#ifdef TIMER_X86 -#define TIMER -extern ulong64 _rdtsc(void); -extern void _reset(void); -ulong64 rdtsc(void) { return _rdtsc(); } -void reset(void) { _reset(); } + #include "tommath.h" #endif #ifdef TIMER -#ifndef TIMER_X86 ulong64 _tt; void reset(void) { _tt = clock(); } ulong64 rdtsc(void) { return clock() - _tt; } #endif -#endif #ifndef DEBUG int _ifuncs; @@ -87,19 +77,19 @@ 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; - + #ifdef TIMER int n; ulong64 tt; -#endif - +#endif + mp_init(&a); mp_init(&b); mp_init(&c); mp_init(&d); mp_init(&e); mp_init(&f); - + #ifdef DEBUG mp_read_radix(&a, "347743159439876626079252796797422223177535447388206607607181663903045907591201940478223621722118173270898487582987137708656414344685816179420855160986340457973820182883508387588163122354089264395604796675278966117567294812714812796820596564876450716066283126720010859041484786529056457896367683122960411136319", 10); mp_read_radix(&b, "347743159439876626079252796797422223177535447388206607607181663903045907591201940478223621722118173270898487582987137708656414344685816179420855160986340457973820182883508387588163122354089264395604796675278966117567294812714812796820596564876450716066283126720010859041484786529056457896367683122960411136318", 10); @@ -123,10 +113,10 @@ int main(void) mp_exptmod(&c, &b, &a, &d); dump_timings(); return 0; -#endif - -#ifdef TIMER -goto expt; +#endif + +#ifdef TIMER + printf("CLOCKS_PER_SEC == %lu\n", CLOCKS_PER_SEC); mp_read_radix(&a, "340282366920938463463374607431768211455", 10); mp_read_radix(&b, "340282366920938463463574607431768211455", 10); while (a.used * DIGIT_BIT < 8192) { @@ -135,7 +125,7 @@ goto expt; mp_add(&a, &b, &c); } tt = rdtsc(); - printf("Adding %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)1000000)); + printf("Adding %d-bit took %f ticks\n", mp_count_bits(&a), (double)tt / ((double)1000000)); mp_sqr(&a, &a); mp_sqr(&b, &b); } @@ -148,7 +138,7 @@ goto expt; mp_sub(&a, &b, &c); } tt = rdtsc(); - printf("Subtracting %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)1000000)); + printf("Subtracting %d-bit took %f ticks\n", mp_count_bits(&a), (double)tt / ((double)1000000)); mp_sqr(&a, &a); mp_sqr(&b, &b); } @@ -156,25 +146,25 @@ goto expt; mp_read_radix(&a, "340282366920938463463374607431768211455", 10); while (a.used * DIGIT_BIT < 8192) { reset(); - for (rr = 0; rr < 100000; rr++) { + for (rr = 0; rr < 1000000; rr++) { mp_sqr(&a, &b); } tt = rdtsc(); - printf("Squaring %d-bit took %lu cycles\n", mp_count_bits(&a), tt / ((ulong64)100000)); + printf("Squaring %d-bit took %f ticks\n", mp_count_bits(&a), (double)tt / ((double)1000000)); mp_copy(&b, &a); } mp_read_radix(&a, "340282366920938463463374607431768211455", 10); while (a.used * DIGIT_BIT < 8192) { reset(); - for (rr = 0; rr < 100000; rr++) { + for (rr = 0; rr < 1000000; rr++) { mp_mul(&a, &a, &b); } tt = rdtsc(); - printf("Multiplying %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)100000)); + printf("Multiplying %d-bit took %f ticks\n", mp_count_bits(&a), (double)tt / ((double)1000000)); mp_copy(&b, &a); } -expt: + { char *primes[] = { "17933601194860113372237070562165128350027320072176844226673287945873370751245439587792371960615073855669274087805055507977323024886880985062002853331424203", @@ -211,7 +201,7 @@ expt: draw(&d); exit(0); } - printf("Exponentiating %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)100)); + printf("Exponentiating %d-bit took %f ticks\n", mp_count_bits(&a), (double)tt / ((double)100)); } } @@ -229,14 +219,14 @@ expt: printf("Failed to invert\n"); return 0; } - printf("Inverting mod %d-bit took %llu cycles\n", mp_count_bits(&a), tt / ((ulong64)100)); + printf("Inverting mod %d-bit took %f ticks\n", mp_count_bits(&a), (double)tt / ((double)100)); mp_sqr(&a, &a); mp_sqr(&b, &b); } return 0; -#endif +#endif inv_n = expt_n = lcm_n = gcd_n = add_n = sub_n = mul_n = div_n = sqr_n = mul2d_n = div2d_n = 0; for (;;) { diff --git a/etc/makefile b/etc/makefile index ed8f915..7a98e09 100644 --- a/etc/makefile +++ b/etc/makefile @@ -1 +1,15 @@ -CFLAGS += -I../ -Wall -W -Wshadow -ansi -O3 -fomit-frame-pointer -funroll-loops ../bn.c \ No newline at end of file +CFLAGS += -Wall -W -Wshadow -O3 -fomit-frame-pointer -funroll-loops + +pprime: pprime.o + $(CC) pprime.o -ltommath -o pprime + +tune: tune.o + $(CC) tune.o -ltommath -o tune + +mersenne: mersenne.o + $(CC) mersenne.o -ltommath -o mersenne + +clean: + rm -f *.o pprime tune mersenne + + diff --git a/etc/mersenne.c b/etc/mersenne.c index b6bbd51..04b2d98 100644 --- a/etc/mersenne.c +++ b/etc/mersenne.c @@ -3,148 +3,152 @@ * Tom St Denis, tomstdenis@iahu.ca */ #include -#include +#include -int is_mersenne(long s, int *pp) +int +is_mersenne (long s, int *pp) { - mp_int n, u, mu; - int res, k; - long ss; - + mp_int n, u, mu; + int res, k; + long ss; + *pp = 0; - - if ((res = mp_init(&n)) != MP_OKAY) { - return res; + + if ((res = mp_init (&n)) != MP_OKAY) { + return res; } - - if ((res = mp_init(&u)) != MP_OKAY) { - goto __N; + + if ((res = mp_init (&u)) != MP_OKAY) { + goto __N; } - - if ((res = mp_init(&mu)) != MP_OKAY) { - goto __U; + + if ((res = mp_init (&mu)) != MP_OKAY) { + goto __U; } - + /* n = 2^s - 1 */ - mp_set(&n, 1); + mp_set (&n, 1); ss = s; while (ss--) { - if ((res = mp_mul_2(&n, &n)) != MP_OKAY) { - goto __MU; - } + if ((res = mp_mul_2 (&n, &n)) != MP_OKAY) { + goto __MU; + } } - if ((res = mp_sub_d(&n, 1, &n)) != MP_OKAY) { - goto __MU; + if ((res = mp_sub_d (&n, 1, &n)) != MP_OKAY) { + goto __MU; } - + /* setup mu */ - if ((res = mp_reduce_setup(&mu, &n)) != MP_OKAY) { - goto __MU; + if ((res = mp_reduce_setup (&mu, &n)) != MP_OKAY) { + goto __MU; } - + /* set u=4 */ - mp_set(&u, 4); - + mp_set (&u, 4); + /* for k=1 to s-2 do */ for (k = 1; k <= s - 2; k++) { - /* u = u^2 - 2 mod n */ - if ((res = mp_sqr(&u, &u)) != MP_OKAY) { - goto __MU; - } - if ((res = mp_sub_d(&u, 2, &u)) != MP_OKAY) { - goto __MU; - } - - /* make sure u is positive */ - if (u.sign == MP_NEG) { - if ((res = mp_add(&u, &n, &u)) != MP_OKAY) { - goto __MU; - } - } - - /* reduce */ - if ((res = mp_reduce(&u, &n, &mu)) != MP_OKAY) { - goto __MU; + /* u = u^2 - 2 mod n */ + if ((res = mp_sqr (&u, &u)) != MP_OKAY) { + goto __MU; + } + if ((res = mp_sub_d (&u, 2, &u)) != MP_OKAY) { + goto __MU; + } + + /* make sure u is positive */ + if (u.sign == MP_NEG) { + if ((res = mp_add (&u, &n, &u)) != MP_OKAY) { + goto __MU; } + } + + /* reduce */ + if ((res = mp_reduce (&u, &n, &mu)) != MP_OKAY) { + goto __MU; + } } - + /* if u == 0 then its prime */ - if (mp_iszero(&u) == 1) { - *pp = 1; + if (mp_iszero (&u) == 1) { + *pp = 1; } - + res = MP_OKAY; -__MU: mp_clear(&mu); -__U: mp_clear(&u); -__N: mp_clear(&n); +__MU:mp_clear (&mu); +__U:mp_clear (&u); +__N:mp_clear (&n); return res; } /* square root of a long < 65536 */ -long i_sqrt(long x) +long +i_sqrt (long x) { - long x1, x2; - - x2 = 16; - do { - x1 = x2; - x2 = x1 - ((x1 * x1) - x)/(2*x1); - } while (x1 != x2); - - if (x1*x1 > x) { - --x1; - } - - return x1; + long x1, x2; + + x2 = 16; + do { + x1 = x2; + x2 = x1 - ((x1 * x1) - x) / (2 * x1); + } while (x1 != x2); + + if (x1 * x1 > x) { + --x1; + } + + return x1; } /* is the long prime by brute force */ -int isprime(long k) +int +isprime (long k) { - long y, z; - - y = i_sqrt(k); - for (z = 2; z <= y; z++) { - if ((k % z) == 0) return 0; - } - return 1; -} - + long y, z; -int main(void) -{ - int pp; - long k; - clock_t tt; - - k = 3; - - for (;;) { - /* start time */ - tt = clock(); - - /* test if 2^k - 1 is prime */ - if (is_mersenne(k, &pp) != MP_OKAY) { - printf("Whoa error\n"); - return -1; - } - - if (pp == 1) { - /* count time */ - tt = clock() - tt; - - /* display if prime */ - printf("2^%-5ld - 1 is prime, test took %ld ticks\n", k, tt); - } - - /* goto next odd exponent */ - k += 2; - - /* but make sure its prime */ - while (isprime(k) == 0) { - k += 2; - } - } - return 0; + y = i_sqrt (k); + for (z = 2; z <= y; z++) { + if ((k % z) == 0) + return 0; + } + return 1; } + +int +main (void) +{ + int pp; + long k; + clock_t tt; + + k = 3; + + for (;;) { + /* start time */ + tt = clock (); + + /* test if 2^k - 1 is prime */ + if (is_mersenne (k, &pp) != MP_OKAY) { + printf ("Whoa error\n"); + return -1; + } + + if (pp == 1) { + /* count time */ + tt = clock () - tt; + + /* display if prime */ + printf ("2^%-5ld - 1 is prime, test took %ld ticks\n", k, tt); + } + + /* goto next odd exponent */ + k += 2; + + /* but make sure its prime */ + while (isprime (k) == 0) { + k += 2; + } + } + return 0; +} diff --git a/etc/pprime.c b/etc/pprime.c index 4943088..c136901 100644 --- a/etc/pprime.c +++ b/etc/pprime.c @@ -8,220 +8,286 @@ #include "bn.h" /* fast square root */ -static mp_digit i_sqrt(mp_word x) +static mp_digit +i_sqrt (mp_word x) { - mp_word x1, x2; - - x2 = x; - do { - x1 = x2; - x2 = x1 - ((x1 * x1) - x)/(2*x1); - } while (x1 != x2); - - if (x1*x1 > x) { - --x1; - } - - return x1; -} - + mp_word x1, x2; + + x2 = x; + do { + x1 = x2; + x2 = x1 - ((x1 * x1) - x) / (2 * x1); + } while (x1 != x2); + + if (x1 * x1 > x) { + --x1; + } + + return x1; +} + /* generates a prime digit */ -static mp_digit prime_digit() +static mp_digit +prime_digit () { - mp_digit r, x, y, next; - - /* make a DIGIT_BIT-bit random number */ - for (r = x = 0; x < DIGIT_BIT; x++) { - r = (r << 1) | (rand() & 1); - } - - /* now force it odd */ - r |= 1; - - /* force it to be >30 */ - if (r < 30) { - r += 30; - } - - /* get square root, since if 'r' is composite its factors must be < than this */ - y = i_sqrt(r); - next = (y+1)*(y+1); + mp_digit r, x, y, next; - do { - r += 2; /* next candidate */ - - /* update sqrt ? */ - if (next <= r) { - ++y; - next = (y+1)*(y+1); - } + /* make a DIGIT_BIT-bit random number */ + for (r = x = 0; x < DIGIT_BIT; x++) { + r = (r << 1) | (rand () & 1); + } - /* loop if divisible by 3,5,7,11,13,17,19,23,29 */ - if ((r % 3) == 0) { x = 0; continue; } - if ((r % 5) == 0) { x = 0; continue; } - if ((r % 7) == 0) { x = 0; continue; } - if ((r % 11) == 0) { x = 0; continue; } - if ((r % 13) == 0) { x = 0; continue; } - if ((r % 17) == 0) { x = 0; continue; } - if ((r % 19) == 0) { x = 0; continue; } - if ((r % 23) == 0) { x = 0; continue; } - if ((r % 29) == 0) { x = 0; continue; } - - /* now check if r is divisible by x + k={1,7,11,13,17,19,23,29} */ - for (x = 30; x <= y; x += 30) { - if ((r % (x+1)) == 0) { x = 0; break; } - if ((r % (x+7)) == 0) { x = 0; break; } - if ((r % (x+11)) == 0) { x = 0; break; } - if ((r % (x+13)) == 0) { x = 0; break; } - if ((r % (x+17)) == 0) { x = 0; break; } - if ((r % (x+19)) == 0) { x = 0; break; } - if ((r % (x+23)) == 0) { x = 0; break; } - if ((r % (x+29)) == 0) { x = 0; break; } + /* now force it odd */ + r |= 1; + + /* force it to be >30 */ + if (r < 30) { + r += 30; + } + + /* get square root, since if 'r' is composite its factors must be < than this */ + y = i_sqrt (r); + next = (y + 1) * (y + 1); + + do { + r += 2; /* next candidate */ + + /* update sqrt ? */ + if (next <= r) { + ++y; + next = (y + 1) * (y + 1); + } + + /* loop if divisible by 3,5,7,11,13,17,19,23,29 */ + if ((r % 3) == 0) { + x = 0; + continue; + } + if ((r % 5) == 0) { + x = 0; + continue; + } + if ((r % 7) == 0) { + x = 0; + continue; + } + if ((r % 11) == 0) { + x = 0; + continue; + } + if ((r % 13) == 0) { + x = 0; + continue; + } + if ((r % 17) == 0) { + x = 0; + continue; + } + if ((r % 19) == 0) { + x = 0; + continue; + } + if ((r % 23) == 0) { + x = 0; + continue; + } + if ((r % 29) == 0) { + x = 0; + continue; + } + + /* now check if r is divisible by x + k={1,7,11,13,17,19,23,29} */ + for (x = 30; x <= y; x += 30) { + if ((r % (x + 1)) == 0) { + x = 0; + break; } - } while (x == 0); - - return r; + if ((r % (x + 7)) == 0) { + x = 0; + break; + } + if ((r % (x + 11)) == 0) { + x = 0; + break; + } + if ((r % (x + 13)) == 0) { + x = 0; + break; + } + if ((r % (x + 17)) == 0) { + x = 0; + break; + } + if ((r % (x + 19)) == 0) { + x = 0; + break; + } + if ((r % (x + 23)) == 0) { + x = 0; + break; + } + if ((r % (x + 29)) == 0) { + x = 0; + break; + } + } + } while (x == 0); + + return r; } /* makes a prime of at least k bits */ -int pprime(int k, int li, mp_int *p, mp_int *q) +int +pprime (int k, int li, mp_int * p, mp_int * q) { - mp_int a, b, c, n, x, y, z, v; - int res, ii; - static const mp_digit bases[] = { 2, 3, 5, 7, 11, 13, 17, 19 }; - - /* single digit ? */ - if (k <= (int)DIGIT_BIT) { - mp_set(p, prime_digit()); - return MP_OKAY; - } - - if ((res = mp_init(&c)) != MP_OKAY) { - return res; - } - - if ((res = mp_init(&v)) != MP_OKAY) { - goto __C; - } - - /* product of first 50 primes */ - if ((res = mp_read_radix(&v, "19078266889580195013601891820992757757219839668357012055907516904309700014933909014729740190", 10)) != MP_OKAY) { - goto __V; - } + mp_int a, b, c, n, x, y, z, v; + int res, ii; + static const mp_digit bases[] = { 2, 3, 5, 7, 11, 13, 17, 19 }; - if ((res = mp_init(&a)) != MP_OKAY) { - goto __V; - } - - /* set the prime */ - mp_set(&a, prime_digit()); - - if ((res = mp_init(&b)) != MP_OKAY) { - goto __A; - } - - if ((res = mp_init(&n)) != MP_OKAY) { - goto __B; - } - - if ((res = mp_init(&x)) != MP_OKAY) { - goto __N; - } + /* single digit ? */ + if (k <= (int) DIGIT_BIT) { + mp_set (p, prime_digit ()); + return MP_OKAY; + } - if ((res = mp_init(&y)) != MP_OKAY) { - goto __X; - } + if ((res = mp_init (&c)) != MP_OKAY) { + return res; + } - if ((res = mp_init(&z)) != MP_OKAY) { - goto __Y; - } + if ((res = mp_init (&v)) != MP_OKAY) { + goto __C; + } - /* now loop making the single digit */ - while (mp_count_bits(&a) < k) { - printf("prime has %4d bits left\r", k - mp_count_bits(&a)); fflush(stdout); - top: - mp_set(&b, prime_digit()); - - /* now compute z = a * b * 2 */ - if ((res = mp_mul(&a, &b, &z)) != MP_OKAY) { /* z = a * b */ - goto __Z; - } - - if ((res = mp_copy(&z, &c)) != MP_OKAY) { /* c = a * b */ - goto __Z; - } - - if ((res = mp_mul_2(&z, &z)) != MP_OKAY) { /* z = 2 * a * b */ - goto __Z; - } - - /* n = z + 1 */ - if ((res = mp_add_d(&z, 1, &n)) != MP_OKAY) { /* n = z + 1 */ - goto __Z; - } - - /* check (n, v) == 1 */ - if ((res = mp_gcd(&n, &v, &y)) != MP_OKAY) { /* y = (n, v) */ - goto __Z; - } - - if (mp_cmp_d(&y, 1) != MP_EQ) goto top; - - /* now try base x=bases[ii] */ - for (ii = 0; ii < li; ii++) { - mp_set(&x, bases[ii]); - - /* compute x^a mod n */ - if ((res = mp_exptmod(&x, &a, &n, &y)) != MP_OKAY) { /* y = x^a mod n */ - goto __Z; - } - - /* if y == 1 loop */ - if (mp_cmp_d(&y, 1) == MP_EQ) continue; - - /* now x^2a mod n */ - if ((res = mp_sqrmod(&y, &n, &y)) != MP_OKAY) { /* y = x^2a mod n */ - goto __Z; - } - - if (mp_cmp_d(&y, 1) == MP_EQ) continue; - - /* compute x^b mod n */ - if ((res = mp_exptmod(&x, &b, &n, &y)) != MP_OKAY) { /* y = x^b mod n */ - goto __Z; - } - - /* if y == 1 loop */ - if (mp_cmp_d(&y, 1) == MP_EQ) continue; - - /* now x^2b mod n */ - if ((res = mp_sqrmod(&y, &n, &y)) != MP_OKAY) { /* y = x^2b mod n */ - goto __Z; - } - - if (mp_cmp_d(&y, 1) == MP_EQ) continue; + /* product of first 50 primes */ + if ((res = + mp_read_radix (&v, + "19078266889580195013601891820992757757219839668357012055907516904309700014933909014729740190", + 10)) != MP_OKAY) { + goto __V; + } - /* compute x^c mod n == x^ab mod n */ - if ((res = mp_exptmod(&x, &c, &n, &y)) != MP_OKAY) { /* y = x^ab mod n */ - goto __Z; - } - - /* if y == 1 loop */ - if (mp_cmp_d(&y, 1) == MP_EQ) continue; - - /* now compute (x^c mod n)^2 */ - if ((res = mp_sqrmod(&y, &n, &y)) != MP_OKAY) { /* y = x^2ab mod n */ - goto __Z; - } - - /* y should be 1 */ - if (mp_cmp_d(&y, 1) != MP_EQ) continue; - break; + if ((res = mp_init (&a)) != MP_OKAY) { + goto __V; + } + + /* set the prime */ + mp_set (&a, prime_digit ()); + + if ((res = mp_init (&b)) != MP_OKAY) { + goto __A; + } + + if ((res = mp_init (&n)) != MP_OKAY) { + goto __B; + } + + if ((res = mp_init (&x)) != MP_OKAY) { + goto __N; + } + + if ((res = mp_init (&y)) != MP_OKAY) { + goto __X; + } + + if ((res = mp_init (&z)) != MP_OKAY) { + goto __Y; + } + + /* now loop making the single digit */ + while (mp_count_bits (&a) < k) { + printf ("prime has %4d bits left\r", k - mp_count_bits (&a)); + fflush (stdout); + top: + mp_set (&b, prime_digit ()); + + /* now compute z = a * b * 2 */ + if ((res = mp_mul (&a, &b, &z)) != MP_OKAY) { /* z = a * b */ + goto __Z; + } + + if ((res = mp_copy (&z, &c)) != MP_OKAY) { /* c = a * b */ + goto __Z; + } + + if ((res = mp_mul_2 (&z, &z)) != MP_OKAY) { /* z = 2 * a * b */ + goto __Z; + } + + /* n = z + 1 */ + if ((res = mp_add_d (&z, 1, &n)) != MP_OKAY) { /* n = z + 1 */ + goto __Z; + } + + /* check (n, v) == 1 */ + if ((res = mp_gcd (&n, &v, &y)) != MP_OKAY) { /* y = (n, v) */ + goto __Z; + } + + if (mp_cmp_d (&y, 1) != MP_EQ) + goto top; + + /* now try base x=bases[ii] */ + for (ii = 0; ii < li; ii++) { + mp_set (&x, bases[ii]); + + /* compute x^a mod n */ + if ((res = mp_exptmod (&x, &a, &n, &y)) != MP_OKAY) { /* y = x^a mod n */ + goto __Z; } - - /* no bases worked? */ - if (ii == li) goto top; + + /* if y == 1 loop */ + if (mp_cmp_d (&y, 1) == MP_EQ) + continue; + + /* now x^2a mod n */ + if ((res = mp_sqrmod (&y, &n, &y)) != MP_OKAY) { /* y = x^2a mod n */ + goto __Z; + } + + if (mp_cmp_d (&y, 1) == MP_EQ) + continue; + + /* compute x^b mod n */ + if ((res = mp_exptmod (&x, &b, &n, &y)) != MP_OKAY) { /* y = x^b mod n */ + goto __Z; + } + + /* if y == 1 loop */ + if (mp_cmp_d (&y, 1) == MP_EQ) + continue; + + /* now x^2b mod n */ + if ((res = mp_sqrmod (&y, &n, &y)) != MP_OKAY) { /* y = x^2b mod n */ + goto __Z; + } + + if (mp_cmp_d (&y, 1) == MP_EQ) + continue; + + /* compute x^c mod n == x^ab mod n */ + if ((res = mp_exptmod (&x, &c, &n, &y)) != MP_OKAY) { /* y = x^ab mod n */ + goto __Z; + } + + /* if y == 1 loop */ + if (mp_cmp_d (&y, 1) == MP_EQ) + continue; + + /* now compute (x^c mod n)^2 */ + if ((res = mp_sqrmod (&y, &n, &y)) != MP_OKAY) { /* y = x^2ab mod n */ + goto __Z; + } + + /* y should be 1 */ + if (mp_cmp_d (&y, 1) != MP_EQ) + continue; + break; + } + + /* no bases worked? */ + if (ii == li) + goto top; /* { @@ -236,62 +302,62 @@ int pprime(int k, int li, mp_int *p, mp_int *q) printf("----------------------------------------------------------------\n"); } */ - /* a = n */ - mp_copy(&n, &a); + /* a = n */ + mp_copy (&n, &a); } - + /* get q to be the order of the large prime subgroup */ - mp_sub_d(&n, 1, q); - mp_div_2(q, q); - mp_div(q, &b, q, NULL); + mp_sub_d (&n, 1, q); + mp_div_2 (q, q); + mp_div (q, &b, q, NULL); - mp_exch(&n, p); - - res = MP_OKAY; -__Z: mp_clear(&z); -__Y: mp_clear(&y); -__X: mp_clear(&x); -__N: mp_clear(&n); -__B: mp_clear(&b); -__A: mp_clear(&a); -__V: mp_clear(&v); -__C: mp_clear(&c); - return res; -} + mp_exch (&n, p); - -int main(void) -{ - mp_int p, q; - char buf[4096]; - int k, li; - clock_t t1; - - srand(time(NULL)); - - printf("Enter # of bits: \n"); - fgets(buf, sizeof(buf), stdin); - sscanf(buf, "%d", &k); - - printf("Enter number of bases to try (1 to 8):\n"); - fgets(buf, sizeof(buf), stdin); - sscanf(buf, "%d", &li); - - - mp_init(&p); - mp_init(&q); - - t1 = clock(); - pprime(k, li, &p, &q); - t1 = clock() - t1; - - printf("\n\nTook %ld ticks, %d bits\n", t1, mp_count_bits(&p)); - - mp_toradix(&p, buf, 10); - printf("P == %s\n", buf); - mp_toradix(&q, buf, 10); - printf("Q == %s\n", buf); - - return 0; + res = MP_OKAY; +__Z:mp_clear (&z); +__Y:mp_clear (&y); +__X:mp_clear (&x); +__N:mp_clear (&n); +__B:mp_clear (&b); +__A:mp_clear (&a); +__V:mp_clear (&v); +__C:mp_clear (&c); + return res; } + +int +main (void) +{ + mp_int p, q; + char buf[4096]; + int k, li; + clock_t t1; + + srand (time (NULL)); + + printf ("Enter # of bits: \n"); + fgets (buf, sizeof (buf), stdin); + sscanf (buf, "%d", &k); + + printf ("Enter number of bases to try (1 to 8):\n"); + fgets (buf, sizeof (buf), stdin); + sscanf (buf, "%d", &li); + + + mp_init (&p); + mp_init (&q); + + t1 = clock (); + pprime (k, li, &p, &q); + t1 = clock () - t1; + + printf ("\n\nTook %ld ticks, %d bits\n", t1, mp_count_bits (&p)); + + mp_toradix (&p, buf, 10); + printf ("P == %s\n", buf); + mp_toradix (&q, buf, 10); + printf ("Q == %s\n", buf); + + return 0; +} diff --git a/etc/tune.c b/etc/tune.c new file mode 100644 index 0000000..0d03e77 --- /dev/null +++ b/etc/tune.c @@ -0,0 +1,96 @@ +/* Tune the Karatsuba parameters + * + * Tom St Denis, tomstdenis@iahu.ca + */ +#include +#include + +clock_t +time_mult (void) +{ + clock_t t1; + int x, y; + mp_int a, b, c; + + mp_init (&a); + mp_init (&b); + mp_init (&c); + + t1 = clock (); + for (x = 8; x <= 128; x += 8) { + mp_rand (&a, x); + mp_rand (&b, x); + for (y = 0; y < 10000; y++) { + mp_mul (&a, &b, &c); + } + } + mp_clear (&a); + mp_clear (&b); + mp_clear (&c); + return clock () - t1; +} + +clock_t +time_sqr (void) +{ + clock_t t1; + int x, y; + mp_int a, b; + + mp_init (&a); + mp_init (&b); + + t1 = clock (); + for (x = 8; x <= 128; x += 8) { + mp_rand (&a, x); + for (y = 0; y < 10000; y++) { + mp_sqr (&a, &b); + } + } + mp_clear (&a); + mp_clear (&b); + return clock () - t1; +} + +int +main (void) +{ + int best_mult, best_square; + clock_t best, ti; + + best_mult = best_square = 0; + + /* tune multiplication first */ + best = CLOCKS_PER_SEC * 1000; + for (KARATSUBA_MUL_CUTOFF = 8; KARATSUBA_MUL_CUTOFF <= 128; + KARATSUBA_MUL_CUTOFF++) { + ti = time_mult (); + printf ("%4d : %9lu\r", KARATSUBA_MUL_CUTOFF, ti); + fflush (stdout); + if (ti < best) { + printf ("New best: %lu, %d \n", ti, KARATSUBA_MUL_CUTOFF); + best = ti; + best_mult = KARATSUBA_MUL_CUTOFF; + } + } + + /* tune squaring */ + best = CLOCKS_PER_SEC * 1000; + for (KARATSUBA_SQR_CUTOFF = 8; KARATSUBA_SQR_CUTOFF <= 128; + KARATSUBA_SQR_CUTOFF++) { + ti = time_sqr (); + printf ("%4d : %9lu\r", KARATSUBA_SQR_CUTOFF, ti); + fflush (stdout); + if (ti < best) { + printf ("New best: %lu, %d \n", ti, KARATSUBA_SQR_CUTOFF); + best = ti; + best_square = KARATSUBA_SQR_CUTOFF; + } + } + + printf + ("\n\n\nKaratsuba Multiplier Cutoff: %d\nKaratsuba Squaring Cutoff: %d\n", + best_mult, best_square); + + return 0; +} diff --git a/poly.c b/etclib/poly.c similarity index 100% rename from poly.c rename to etclib/poly.c diff --git a/poly.h b/etclib/poly.h similarity index 100% rename from poly.h rename to etclib/poly.h diff --git a/ltmtest.exe b/ltmtest.exe new file mode 100644 index 0000000000000000000000000000000000000000..cc5c1f527ef8bd2b5390f81aee0cade8570440bf GIT binary patch literal 49664 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%0D1{ zuyn=Tt%d2paQZQZt7^hfI2ezKuRu1PsH+pxq-vbRzQ*B%~6nNerNaN6N<<+`lE9f!wN zjyj_%*ITV4XZ?}3wXJMSkF~nBzv(li&^(YvW|dQGms$Cjm2^&wW0~fcC-Q&bKIF7- z0nJ;+_>;?U3ti)ORQr}xtG68;W_k3)rAA`y>zzkD$+qkoV{$X^+bs|8>NwfnTI1$t zgiUzW{>8q?oub{EdB5W`d#rExSxKN}x8G^vt2>FW?u_MDMKbSm9=6kHP)fds0}sib zg)60Ce}}1opvGrNW7U2ynAOhdZAwM$QyWhJIaRau+{sAlH1H%iu2EfAkM3owC*l^ zjcu^s24G{Hs=Mt?+y5Hw+#(WWrAFPbrG-muv^kGWVY*Hv5(EH&QFbY z%x6~tB-gB_{|1QZK_EUWAa3%3c$yEy;NAn`Y1xyO`2N=`$u3b5k6Z?L@O@=n~f zxSFxv`6V*g)M014jlBB$(VJ^mMI#oo^D@` zzli5sMuan~I%e6+YhKS!4Quzi*XkZYgDfIEW>4S3N*seJMLtV=$;WuqVSp6gk)=l- zXqFTjPLn#F=4WncCbxwgj*WW_t!mfN_&B{&e8Pf(7V@RTs|i8qcC zmyhxGcdfg2;E1ce8-85rYFzS|V0g$38qA3tMzD8m|AdKCcvHHGQ-XU94=W<4Wj|MJ z&ns&0(D?9G6R&U?+x}G(uW(th{i`MM%8GJ5v$_^0Q=nW+*6~5$%f&13Xll*8Xm!`g znCNwDxF>&hn3U`E;EDEvca!ty@IjEHe5*nCHr)(CO7>U4uboKeW# znX-#oyGZ8I!ve{>5~HT8|u6_Sm!4gp zN#O_KkDT38hQUB-qI|=H?e|K2z6CWaLbSsjUm$HPftqu2tVXX`W;7#k)M4$u$Q^u! zOeon=dj8KOd+T9$==mJkL-r2EYof{dCeKD0V%&kpOaN+(bE(&L&CQuiy#KuB<~6=R zaONswvme1!5Vs$7f5};wl2DIdh<}@KZ=qQ@J0B?cF32#Wkk4bbFp(@x4px~-35#sE znG3e)oR$|7CJOe12m&zGCgQ7IgK{tEhe^yYF5raB8HIbyMcMqrgqtBiV?W_c2{L@x zfF{{@!TdVWwmnU!aQ7~+iD7)3CjRz;F3RQ~L=4PnG2Fv+T+$PGA)bBW4rsO(dMTuS z%~D}49)8|jl+8c5k5NyGa>Ax}7=J=OYye80V=GipbkIKi^@<34#}Mqgi5~m2p}=C(F_J`YLm{}-$5LefXT!w4ocPiQ{?Jcub z@CHhApM7VUMtXkkp#N{~7dbWQ|Gp{ozUXvB&q{7%;}id9-GAt!XL-vAUG(AV0Y+mG-P1 z&Wv*F{#jAGZI}Jbf)A!yoy%cgClsvQj+ph`UhW3sT`Pap99lB_zneolxM<>A*I(za zV~@WU?$6;b$Y+j`Z+#E#MBIN!*X}gbMp`Nc6Qvl}8r} zNwPgWRRUU}gXQMpTjK0UTGx&+Nx!~C_L5+J z|ZqK#Ye1NJ2Qwc=txD}y~`Iw$?pe4pI2!|Bn@6+-ZnhhbIzA%tq3&mlIo8% z8W_c#@(_Dl=A2k|wllsWe+t_x*$6;+!x=eSDpG;dmuwqf9?pK2rSwc?_Ml?9M#{a$ z9cy4=^3aW22kD@huXAm0>hHpSq2IQ~FWLW>puj{5xaadBea|XPq93L9Cb{bu9or&O zv#39lgAGRqs94?r$5jCI8$SCL5Is#%+2kn&VGWAYb^S;Uv5Wv+u?*_@<2Nh zx}VnnChrKQx8oxHuKP4Z&3?O~H^%PRg%h&dcG$Dt%U>~K!8IekleO61nDw61w!`T# zz-J)n6+@?gBPGp4uSGm# zCeCC;Snf=VnjP-UdVbk+{b4UD>C4Y=k7a;r@`M;?ml5X|)m zbhIrO6?6ZF>mQ<6d9j$hPt5%T#S90|jIDUIfREhD>Mx%=YegV$^SpMIoJ(7|b9Drt zx*}S$gXejrcr7Pd_wN7@8vSMc2auwReCt-E>LuompG;XwUd?X2)gUz#(UL+B3FTxmqnnzzBY<}{tzqn*g_lh2c654S6R zfB83e_wV_ZBg!&6>{j=(ul*C|=1<-uvxKqvW1T&iav@6R=lw!_eD^zI$bZU%^96wf z)bogb^(E-w{Pi9k)UM7&@-vSJ_useg^48)5puBgTLII_h`dg!~v3;$Z5eR$7vA)(V z=Bgvw*Xo+9gZf%u)kW81R_CAi*1+Pg&+sI%;;R25)qY2(8$i~Hty_4L6xIzs|0pYL zebu_Z^;NBiD@pclJ#c!t-MZOcgApC8QykuwW@jgtC$_$tjVw65JVHG2szV`xna4Jb zeQouC8gp~JE=q&dPs}@uhFvkyf5WP=C$=9jsjhX|@_|%Ir1HWX%Hkh9d#&((Ak>H1C?k@xXMqb%3F-S3TM6!w9gm#QCv>#H* z?(>{ZK&Vb>kQr!8Ij2Pldk&EwV-Vmc$XP_nUdzkz$48cU{d3O7@*-W&w=D^fMIXi5 zb3+TW0U=75dVv#D*DEUa5N22F*8atwXx`i1J_31w9}g>%2VuazQhU!@1% zF3RJJL&?LwVyI-*Pz;dD#4fD{uOcbKgQx5sW&d#N3bG^W-*~Z^`YjJ>+IUSoj9G z@r3u5#&d(8DfHO7e|-7vyo}`{CIkA`PZ!=E@pi9KJ3EbituJ5@A!k}7Gvfu>%ZyD{ z?nY9I^ATq%-une-#^&&v^{&%?ZPadkir=@gchtxdCA?F>uxV_Qy;_TwxxZOb^96$XCBSY;4--Sz!W`;Vjf8N#5` zy3z<_P-1TQUD15RgT||SLgTMIzURzvC90VH5^c@tTN>5(kV20_;FxvNg$|6mH@m-54#PX3-*R`$f&xQHi6~_;= ziWTuD1X1^kR4~7gXm3+ke33VhLifaS>)MIbGx!)^Zsy#3+jnxn`}vt!inGm3<^B<& z&}}9PPViH;hnI{8Cmw0#{*H*9%PZjOZ9nX1!tb_sh)^e15Kz?eS|x9y1m(6n1GX&K;^#T0;AADVGCk}+ zK4LvEQO?=mZQI21NLvG84TH-jnZXMny2K15FSlCuo^x)*nY#IOX9~~M zPko{2zZe!jW!if|?eWB4fbxMEud?%dkdpEhN1bZ7{29Qu6&M?f+Nar=YshM8?rQpb(T8-O~?C6y-#;Ds%nZmco z%~#zQm8gN=R9M!i-jhA9ui4CIl-g)gw~)HOpK79-!a-E#2*Lhpfv)fZK%aq#o5I2I zsE=m&uNLklh=+w@+Y$E=4e+bYbGQFjdshS7R27Bq+P9<&lPwBK4Ks|NnGKP2h=z(J zZv!(!8{4x2iN?gpOHS`S_ndd`dH3hs^Uk|{FOml7WBoq_la6J_L`IvF z>oW0SH@R-W+$mdQ?!t!Oh~Sx(5V!D%(t`BlR`JVg9`mo7AcymY{8jOK?e-D)NDT&zSXc`5iwttUqeSt zkQWOhE)3{krwxP!(M#&{Wxk#&Z)N@H!In?W*ze%_aEaRZ5fIV!mB{ zqf^hII(o4^8_anzT8iz+@e$`h#dvYnpnFl$5+A}X>Jy-xv^fzYy&r-qi*Jf*FkEffHexn1t3g=oJ$dWd2MDT1jfrQn-u z-ogGAP#B7lOeh7#3{#+3E*i9);&=fJ;0 zJQ6Z_qjSrmxB{7EnP^<1I%Lx$k5Wrgl$f@@;s(%K5_6il_4ArwO#Ql(RXH*Qw ztdCQzaKPc*$lt@aDhzvT%;BmPL2EbBu($EsTQ}jckw@k=Vg;NuA~P(L=`!Nxh72vN zl;HP<;D*TF2s~KWC;zGe)ox)%kK@xn|2)KX%80b~A_G6i8dl{;x0{Q5tEL65A+A-i zF`)yj>n_+|x$a6d5kJZ}VeOSq`>s#+TIbdER;QZ1VHd77gmI0fA)FVRR0l^hoH*aV z%1GgS>JL9IWL01EV4{xbcd9+?RYoT+vkOt;%Nd6YyX06;1o^9JxQ_yc_8Erko@*%k ziAp$C3a14T-S?upkbL3RRfGXfR?jyNd*-N>TaC25T_$S>&CWi4flC{PdOepRDw1}p z=NIVI{{+;VXAM^c@`A-{ zIsdXEB(fzpXeknT!AcVnYGmY6~WGz`SV6r}F$!3HL(@CK6{X9!8%wNzl zhjkOtLLqIhw_bBW0 zbqv+9!=pK#ql_7_^8@YoD%S2-lyIK;qC0!6{h>ci58`zJ*d=C2(U~v0ex^_$ZcGyK z7G*p`3*!~258Inap_IrX+c6W!rrlKn{G(AGPaZ|SFCNc1KBoWUId?SpkOpT{XpX4Y z8_yBO;G|D~HI|RN@$BOMt%NHFE+F24KBa^5-HALec=+?D7TAMhbm5LdaWu+`fDz;- zN@+$uJpQ{akeN_Dg{9OQ(o=;dtWH@dqlb8HABv1DFyE^_-q5HMbif`FX5x1)pm~?b z29wYQxP;6n2>5tA+YHl9#&QFo8P8uUk!s~B{CZw5pXjY3qdK&FB&Q8v0{!_UbX5-V zg#k|Rt-l8!T_?TXun3_D=qAvYCZSVXodvxf^rK1Wyj`~F1*+SZx>?9YIs|^Y74;|Mhu4k% zqpsugoc0mYZT+rfyu7a=bc>$z(%Z9l>=oH=$k$$n(m!{3Yr$LeiO5V3z}pJm`h6nP zJpgYAyv=PQBX{h6Ixt*h$9|E~+YauKjdpFX?Yrx-jNMu_d zKz0*&i`qr@S+X|9`HWgv028F&YtCjL6WQz}GN{jK&aMSLm`wjs{Rp;)$N!`SL^5!R zqj72qYX#mss%!_!hDPBkubQUgWno6Zbiho&T!08z32*{70_p+uQn77-oq*$j5TF+j z0o(wL9H=iHU)Z^c%CyUu^<@VAFs&fOgIXpE^$%g!MI{O=yE)R3)9QLxZN(a&1 zItOxZviEe3n)TJC6}Hj}kL0Sbmx1=NuB5EY=5TpTHrt}2{Cub6vN>Iq4$19iU+Scd zr5?s^>ejg=iLu@K@)4Pb_2p8zTLKkplHTcp$aRz-CpbMDT#~)SR_^sk8*RK|l#kbx zdn*)5p5XL)+>AXd;G~4YUkPrHt4!k8_9Nvzm0r@7v4WD40#9Xm0b}p5MWq!9Ng0*V;<%T_#@_^`1@mX2)z}2c8VyUWFE6()duBE# Ma(y5E{F5#42L#$CUjP6A literal 0 HcmV?d00001 diff --git a/makefile b/makefile index 7567d22..5f6bcc6 100644 --- a/makefile +++ b/makefile @@ -1,18 +1,56 @@ -CC = gcc -CFLAGS += -Wall -W -Wshadow -ansi -O3 -fomit-frame-pointer -funroll-loops +CFLAGS += -I./ -Wall -W -Wshadow -O3 -fomit-frame-pointer -funroll-loops -VERSION=0.11 +VERSION=0.12 -default: test +default: libtommath.a -test: bn.o demo.o - $(CC) bn.o demo.o -o demo +#default files to install +LIBNAME=libtommath.a +HEADERS=tommath.h + +#LIBPATH-The directory for libtomcrypt to be installed to. +#INCPATH-The directory to install the header files for libtommath. +#DATAPATH-The directory to install the pdf docs. +DESTDIR= +LIBPATH=/usr/lib +INCPATH=/usr/include +DATAPATH=/usr/share/doc/libtommath/pdf + +OBJECTS=bncore.o bn_mp_init.o bn_mp_clear.o bn_mp_exch.o bn_mp_grow.o bn_mp_shrink.o \ +bn_mp_clamp.o bn_mp_zero.o bn_mp_set.o bn_mp_set_int.o bn_mp_init_size.o bn_mp_copy.o \ +bn_mp_init_copy.o bn_mp_abs.o bn_mp_neg.o bn_mp_cmp_mag.o bn_mp_cmp.o bn_mp_cmp_d.o \ +bn_mp_rshd.o bn_mp_lshd.o bn_mp_mod_2d.o bn_mp_div_2d.o bn_mp_mul_2d.o bn_mp_div_2.o \ +bn_mp_mul_2.o bn_s_mp_add.o bn_s_mp_sub.o bn_fast_s_mp_mul_digs.o bn_s_mp_mul_digs.o \ +bn_fast_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs.o bn_fast_s_mp_sqr.o bn_s_mp_sqr.o \ +bn_mp_add.o bn_mp_sub.o bn_mp_karatsuba_mul.o bn_mp_mul.o bn_mp_karatsuba_sqr.o \ +bn_mp_sqr.o bn_mp_div.o bn_mp_mod.o bn_mp_add_d.o bn_mp_sub_d.o bn_mp_mul_d.o \ +bn_mp_div_d.o bn_mp_mod_d.o bn_mp_expt_d.o bn_mp_addmod.o bn_mp_submod.o \ +bn_mp_mulmod.o bn_mp_sqrmod.o bn_mp_gcd.o bn_mp_lcm.o bn_fast_mp_invmod.o bn_mp_invmod.o \ +bn_mp_reduce.o bn_mp_montgomery_setup.o bn_fast_mp_montgomery_reduce.o bn_mp_montgomery_reduce.o \ +bn_mp_exptmod_fast.o bn_mp_exptmod.o bn_mp_2expt.o bn_mp_n_root.o bn_mp_jacobi.o bn_reverse.o \ +bn_mp_count_bits.o bn_mp_read_unsigned_bin.o bn_mp_read_signed_bin.o bn_mp_to_unsigned_bin.o \ +bn_mp_to_signed_bin.o bn_mp_unsigned_bin_size.o bn_mp_signed_bin_size.o bn_radix.o \ +bn_mp_xor.o bn_mp_and.o bn_mp_or.o bn_mp_rand.o + +libtommath.a: $(OBJECTS) + $(AR) $(ARFLAGS) libtommath.a $(OBJECTS) + ranlib libtommath.a + +install: libtommath.a docs + install -d -g root -o root $(DESTDIR)$(LIBPATH) + install -d -g root -o root $(DESTDIR)$(INCPATH) + install -d -g root -o root $(DESTDIR)$(DATAPATH) + install -g root -o root $(LIBNAME) $(DESTDIR)$(LIBPATH) + install -g root -o root $(HEADERS) $(DESTDIR)$(INCPATH) + install -g root -o root bn.pdf $(DESTDIR)$(DATAPATH) + +test: libtommath.a demo/demo.o + $(CC) demo/demo.o libtommath.a -o test cd mtest ; gcc $(CFLAGS) mtest.c -o mtest -s - -# builds the x86 demo -test86: - nasm -f coff timer.asm - $(CC) -DDEBUG -DTIMER_X86 $(CFLAGS) bn.c demo.c timer.o -o demo -s + +timing: libtommath.a + $(CC) $(CFLAGS) -DTIMER demo/demo.c libtommath.a -o ltmtest -s + $(CC) $(CFLAGS) -DTIMER -DU_MPI -I./mtest/ demo/demo.c mtest/mpi.c -o mpitest -s docdvi: bn.tex latex bn @@ -22,7 +60,9 @@ docs: docdvi rm -f bn.log bn.aux bn.dvi clean: - rm -f *.pdf *.o *.exe demo mtest/mtest mtest/*.exe etc/*.exe bn.log bn.aux bn.dvi *.log *.s etc/pprime etc/mersenne + rm -f *.pdf *.o *.a etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest \ + bn.log bn.aux bn.dvi *.log *.s + cd etc ; make clean zipup: clean docs cd .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \ diff --git a/mpitest.exe b/mpitest.exe new file mode 100644 index 0000000000000000000000000000000000000000..d32553a3fbc32b23fc6cbd25cabbb350f509c5b0 GIT binary patch literal 58368 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 K`@gT_z<&VQLV%tC literal 0 HcmV?d00001 diff --git a/mtest/mtest.c b/mtest/mtest.c index df5422f..17aef8d 100644 --- a/mtest/mtest.c +++ b/mtest/mtest.c @@ -1,242 +1,242 @@ -/* makes a bignum test harness with NUM tests per operation - * - * the output is made in the following format [one parameter per line] - -operation -operand1 -operand2 -[... operandN] -result1 -result2 -[... resultN] - -So for example "a * b mod n" would be - -mulmod -a -b -n -a*b mod n - -e.g. if a=3, b=4 n=11 then - -mulmod -3 -4 -11 -1 - - */ - -#include -#include -#include -#include "mpi.c" - -FILE *rng; - -void rand_num(mp_int *a) -{ - int n, size; - unsigned char buf[512]; - -top: - size = 1 + ((fgetc(rng)*fgetc(rng)) % 96); - buf[0] = (fgetc(rng)&1)?1:0; - fread(buf+1, 1, size, rng); - for (n = 0; n < size; n++) { - if (buf[n+1]) break; - } - if (n == size) goto top; - mp_read_raw(a, buf, 1+size); -} - -void rand_num2(mp_int *a) -{ - int n, size; - unsigned char buf[512]; - -top: - size = 1 + ((fgetc(rng)*fgetc(rng)) % 96); - buf[0] = (fgetc(rng)&1)?1:0; - fread(buf+1, 1, size, rng); - for (n = 0; n < size; n++) { - if (buf[n+1]) break; - } - if (n == size) goto top; - mp_read_raw(a, buf, 1+size); -} - -int main(void) -{ - int n; - mp_int a, b, c, d, e; - char buf[4096]; - - mp_init(&a); - mp_init(&b); - mp_init(&c); - mp_init(&d); - mp_init(&e); - - rng = fopen("/dev/urandom", "rb"); - if (rng == NULL) { - rng = fopen("/dev/random", "rb"); - if (rng == NULL) { - fprintf(stderr, "\nWarning: stdin used as random source\n\n"); - rng = stdin; - } - } - - for (;;) { - n = fgetc(rng) % 11; - - if (n == 0) { - /* add tests */ - rand_num(&a); - rand_num(&b); - mp_add(&a, &b, &c); - printf("add\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - } else if (n == 1) { - /* sub tests */ - rand_num(&a); - rand_num(&b); - mp_sub(&a, &b, &c); - printf("sub\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - } else if (n == 2) { - /* mul tests */ - rand_num(&a); - rand_num(&b); - mp_mul(&a, &b, &c); - printf("mul\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - } else if (n == 3) { - /* div tests */ - rand_num(&a); - rand_num(&b); - mp_div(&a, &b, &c, &d); - printf("div\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - mp_todecimal(&d, buf); - printf("%s\n", buf); - } else if (n == 4) { - /* sqr tests */ - rand_num(&a); - mp_sqr(&a, &b); - printf("sqr\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - } else if (n == 5) { - /* mul_2d test */ - rand_num(&a); - mp_copy(&a, &b); - n = fgetc(rng) & 63; - mp_mul_2d(&b, n, &b); - mp_todecimal(&a, buf); - printf("mul2d\n"); - printf("%s\n", buf); - printf("%d\n", n); - mp_todecimal(&b, buf); - printf("%s\n", buf); - } else if (n == 6) { - /* div_2d test */ - rand_num(&a); - mp_copy(&a, &b); - n = fgetc(rng) & 63; - mp_div_2d(&b, n, &b, NULL); - mp_todecimal(&a, buf); - printf("div2d\n"); - printf("%s\n", buf); - printf("%d\n", n); - mp_todecimal(&b, buf); - printf("%s\n", buf); - } else if (n == 7) { - /* gcd test */ - rand_num(&a); - rand_num(&b); - a.sign = MP_ZPOS; - b.sign = MP_ZPOS; - mp_gcd(&a, &b, &c); - printf("gcd\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - } else if (n == 8) { - /* lcm test */ - rand_num(&a); - rand_num(&b); - a.sign = MP_ZPOS; - b.sign = MP_ZPOS; - mp_lcm(&a, &b, &c); - printf("lcm\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - } else if (n == 9) { - /* exptmod test */ - rand_num2(&a); - rand_num2(&b); - rand_num2(&c); - a.sign = b.sign = c.sign = 0; - mp_exptmod(&a, &b, &c, &d); - printf("expt\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - mp_todecimal(&d, buf); - printf("%s\n", buf); - } else if (n == 10) { - /* invmod test */ - rand_num2(&a); - rand_num2(&b); - b.sign = MP_ZPOS; - a.sign = MP_ZPOS; - mp_gcd(&a, &b, &c); - if (mp_cmp_d(&c, 1) != 0) continue; - if (mp_cmp_d(&b, 1) == 0) continue; - mp_invmod(&a, &b, &c); - printf("invmod\n"); - mp_todecimal(&a, buf); - printf("%s\n", buf); - mp_todecimal(&b, buf); - printf("%s\n", buf); - mp_todecimal(&c, buf); - printf("%s\n", buf); - } - } - fclose(rng); - return 0; -} +/* makes a bignum test harness with NUM tests per operation + * + * the output is made in the following format [one parameter per line] + +operation +operand1 +operand2 +[... operandN] +result1 +result2 +[... resultN] + +So for example "a * b mod n" would be + +mulmod +a +b +n +a*b mod n + +e.g. if a=3, b=4 n=11 then + +mulmod +3 +4 +11 +1 + + */ + +#include +#include +#include +#include "mpi.c" + +FILE *rng; + +void rand_num(mp_int *a) +{ + int n, size; + unsigned char buf[512]; + +top: + size = 1 + ((fgetc(rng)*fgetc(rng)) % 96); + buf[0] = (fgetc(rng)&1)?1:0; + fread(buf+1, 1, size, rng); + for (n = 0; n < size; n++) { + if (buf[n+1]) break; + } + if (n == size) goto top; + mp_read_raw(a, buf, 1+size); +} + +void rand_num2(mp_int *a) +{ + int n, size; + unsigned char buf[512]; + +top: + size = 1 + ((fgetc(rng)*fgetc(rng)) % 96); + buf[0] = (fgetc(rng)&1)?1:0; + fread(buf+1, 1, size, rng); + for (n = 0; n < size; n++) { + if (buf[n+1]) break; + } + if (n == size) goto top; + mp_read_raw(a, buf, 1+size); +} + +int main(void) +{ + int n; + mp_int a, b, c, d, e; + char buf[4096]; + + mp_init(&a); + mp_init(&b); + mp_init(&c); + mp_init(&d); + mp_init(&e); + + rng = fopen("/dev/urandom", "rb"); + if (rng == NULL) { + rng = fopen("/dev/random", "rb"); + if (rng == NULL) { + fprintf(stderr, "\nWarning: stdin used as random source\n\n"); + rng = stdin; + } + } + + for (;;) { + n = fgetc(rng) % 11; + + if (n == 0) { + /* add tests */ + rand_num(&a); + rand_num(&b); + mp_add(&a, &b, &c); + printf("add\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + } else if (n == 1) { + /* sub tests */ + rand_num(&a); + rand_num(&b); + mp_sub(&a, &b, &c); + printf("sub\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + } else if (n == 2) { + /* mul tests */ + rand_num(&a); + rand_num(&b); + mp_mul(&a, &b, &c); + printf("mul\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + } else if (n == 3) { + /* div tests */ + rand_num(&a); + rand_num(&b); + mp_div(&a, &b, &c, &d); + printf("div\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + mp_todecimal(&d, buf); + printf("%s\n", buf); + } else if (n == 4) { + /* sqr tests */ + rand_num(&a); + mp_sqr(&a, &b); + printf("sqr\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + } else if (n == 5) { + /* mul_2d test */ + rand_num(&a); + mp_copy(&a, &b); + n = fgetc(rng) & 63; + mp_mul_2d(&b, n, &b); + mp_todecimal(&a, buf); + printf("mul2d\n"); + printf("%s\n", buf); + printf("%d\n", n); + mp_todecimal(&b, buf); + printf("%s\n", buf); + } else if (n == 6) { + /* div_2d test */ + rand_num(&a); + mp_copy(&a, &b); + n = fgetc(rng) & 63; + mp_div_2d(&b, n, &b, NULL); + mp_todecimal(&a, buf); + printf("div2d\n"); + printf("%s\n", buf); + printf("%d\n", n); + mp_todecimal(&b, buf); + printf("%s\n", buf); + } else if (n == 7) { + /* gcd test */ + rand_num(&a); + rand_num(&b); + a.sign = MP_ZPOS; + b.sign = MP_ZPOS; + mp_gcd(&a, &b, &c); + printf("gcd\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + } else if (n == 8) { + /* lcm test */ + rand_num(&a); + rand_num(&b); + a.sign = MP_ZPOS; + b.sign = MP_ZPOS; + mp_lcm(&a, &b, &c); + printf("lcm\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + } else if (n == 9) { + /* exptmod test */ + rand_num2(&a); + rand_num2(&b); + rand_num2(&c); + a.sign = b.sign = c.sign = 0; + mp_exptmod(&a, &b, &c, &d); + printf("expt\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + mp_todecimal(&d, buf); + printf("%s\n", buf); + } else if (n == 10) { + /* invmod test */ + rand_num2(&a); + rand_num2(&b); + b.sign = MP_ZPOS; + a.sign = MP_ZPOS; + mp_gcd(&a, &b, &c); + if (mp_cmp_d(&c, 1) != 0) continue; + if (mp_cmp_d(&b, 1) == 0) continue; + mp_invmod(&a, &b, &c); + printf("invmod\n"); + mp_todecimal(&a, buf); + printf("%s\n", buf); + mp_todecimal(&b, buf); + printf("%s\n", buf); + mp_todecimal(&c, buf); + printf("%s\n", buf); + } + } + fclose(rng); + return 0; +} diff --git a/timer.asm b/timer.asm deleted file mode 100644 index b317e3e..0000000 --- a/timer.asm +++ /dev/null @@ -1,34 +0,0 @@ -; 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 diff --git a/timings.txt b/timings.txt new file mode 100644 index 0000000..117b9c5 --- /dev/null +++ b/timings.txt @@ -0,0 +1,39 @@ +CLOCKS_PER_SEC == 1000000 +Adding 128-bit took 0.070000 ticks +Adding 256-bit took 0.100000 ticks +Adding 512-bit took 0.140000 ticks +Adding 1024-bit took 0.210000 ticks +Adding 2048-bit took 0.360000 ticks +Adding 4096-bit took 0.670000 ticks +Subtracting 128-bit took 0.090000 ticks +Subtracting 256-bit took 0.120000 ticks +Subtracting 512-bit took 0.140000 ticks +Subtracting 1024-bit took 0.210000 ticks +Subtracting 2048-bit took 0.330000 ticks +Subtracting 4096-bit took 0.580000 ticks +Squaring 128-bit took 0.320000 ticks +Squaring 256-bit took 0.620000 ticks +Squaring 512-bit took 1.410000 ticks +Squaring 1024-bit took 3.730000 ticks +Squaring 2048-bit took 11.580000 ticks +Squaring 4096-bit took 44.540000 ticks +Multiplying 128-bit took 0.270000 ticks +Multiplying 256-bit took 0.650000 ticks +Multiplying 512-bit took 1.630000 ticks +Multiplying 1024-bit took 5.180000 ticks +Multiplying 2048-bit took 19.210000 ticks +Multiplying 4096-bit took 67.500000 ticks +Exponentiating 513-bit took 2000.000000 ticks +Exponentiating 769-bit took 5200.000000 ticks +Exponentiating 1025-bit took 11400.000000 ticks +Exponentiating 2049-bit took 75100.000000 ticks +Exponentiating 2561-bit took 150000.000000 ticks +Exponentiating 3073-bit took 237800.000000 ticks +Exponentiating 4097-bit took 510600.000000 ticks +Inverting mod 128-bit took 0.000000 ticks +Inverting mod 256-bit took 200.000000 ticks +Inverting mod 512-bit took 300.000000 ticks +Inverting mod 1024-bit took 800.000000 ticks +Inverting mod 2048-bit took 2500.000000 ticks +Inverting mod 4096-bit took 8400.000000 ticks + diff --git a/bn.h b/tommath.h similarity index 83% rename from bn.h rename to tommath.h index 5f39cbd..4ac6173 100644 --- a/bn.h +++ b/tommath.h @@ -21,6 +21,11 @@ #include #include +#undef MIN +#define MIN(x,y) ((x)<(y)?(x):(y)) +#undef MAX +#define MAX(x,y) ((x)>(y)?(x):(y)) + #ifdef __cplusplus extern "C" { #endif @@ -82,9 +87,9 @@ extern "C" { typedef int mp_err; /* you'll have to tune these... */ -#define KARATSUBA_MUL_CUTOFF 80 /* Min. number of digits before Karatsuba multiplication is used. */ -#define KARATSUBA_SQR_CUTOFF 80 /* Min. number of digits before Karatsuba squaring is used. */ -#define MONTGOMERY_EXPT_CUTOFF 40 /* max. number of digits that montgomery reductions will help for */ +extern int KARATSUBA_MUL_CUTOFF, + KARATSUBA_SQR_CUTOFF, + MONTGOMERY_EXPT_CUTOFF; #define MP_PREC 64 /* default digits of precision */ @@ -126,6 +131,9 @@ void mp_set(mp_int *a, mp_digit b); /* set a 32-bit const */ int mp_set_int(mp_int *a, unsigned long b); +/* grow an int to a given size */ +int mp_grow(mp_int *a, int size); + /* init to a given number of digits */ int mp_init_size(mp_int *a, int size); @@ -135,6 +143,9 @@ int mp_copy(mp_int *a, mp_int *b); /* inits and copies, a = b */ int mp_init_copy(mp_int *a, mp_int *b); +/* trim unused digits */ +void mp_clamp(mp_int *a); + /* ---> digit manipulation <--- */ /* right shift by "b" digits */ @@ -144,7 +155,7 @@ void mp_rshd(mp_int *a, int b); int mp_lshd(mp_int *a, int b); /* c = a / 2^b */ -int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); /* b = a/2 */ int mp_div_2(mp_int *a, mp_int *b); @@ -161,6 +172,19 @@ int mp_mod_2d(mp_int *a, int b, mp_int *c); /* computes a = 2^b */ int mp_2expt(mp_int *a, int b); +/* makes a pseudo-random int of a given size */ +int mp_rand(mp_int *a, int digits); + +/* ---> binary operations <--- */ +/* c = a XOR b */ +int mp_xor(mp_int *a, mp_int *b, mp_int *c); + +/* c = a OR b */ +int mp_or(mp_int *a, mp_int *b, mp_int *c); + +/* c = a AND b */ +int mp_and(mp_int *a, mp_int *b, mp_int *c); + /* ---> Basic arithmetic <--- */ /* b = -a */ @@ -178,6 +202,7 @@ 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); @@ -254,7 +279,7 @@ 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 +/* 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]. @@ -266,12 +291,11 @@ 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); /* ---> radix conversion <--- */ - int mp_count_bits(mp_int *a); int mp_unsigned_bin_size(mp_int *a); @@ -298,6 +322,23 @@ int mp_radix_size(mp_int *a, int radix); #define mp_todecimal(M, S) mp_toradix((M), (S), 10) #define mp_tohex(M, S) mp_toradix((M), (S), 16) +/* lowlevel functions, do not call! */ +int s_mp_add(mp_int *a, mp_int *b, mp_int *c); +int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) +int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); +int fast_s_mp_sqr(mp_int *a, mp_int *b); +int s_mp_sqr(mp_int *a, mp_int *b); +int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_karatsuba_sqr(mp_int *a, mp_int *b); +int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); +int fast_mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); +int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y); +void bn_reverse(unsigned char *s, int len); + #ifdef __cplusplus } #endif