From 3b2639b9b5c855203ebedd74caf0f81f57b8fb01 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Mon, 31 May 2004 18:25:22 +0000 Subject: [PATCH] ltm 0.30 orig import --HG-- branch : libtommath-orig extra : convert_revision : 29392f1198b77a2021e78879c1489e84c9e98ef4 --- LICENSE | 4 + bn_error.c | 41 + bn_fast_mp_invmod.c | 143 + bn_fast_mp_montgomery_reduce.c | 167 + bn_fast_s_mp_mul_digs.c | 130 + bn_fast_s_mp_mul_high_digs.c | 98 + bn_fast_s_mp_sqr.c | 139 + bn_mp_2expt.c | 42 + bn_mp_abs.c | 37 + bn_mp_add.c | 47 + bn_mp_add_d.c | 103 + bn_mp_addmod.c | 35 + bn_mp_and.c | 51 + bn_mp_clamp.c | 38 + bn_mp_clear.c | 34 + bn_mp_clear_multi.c | 28 + bn_mp_cmp.c | 37 + bn_mp_cmp_d.c | 38 + bn_mp_cmp_mag.c | 49 + bn_mp_cnt_lsb.c | 47 + bn_mp_copy.c | 62 + bn_mp_count_bits.c | 39 + bn_mp_div.c | 211 + bn_mp_div_2.c | 62 + bn_mp_div_2d.c | 91 + bn_mp_div_3.c | 73 + bn_mp_div_d.c | 102 + bn_mp_dr_is_modulus.c | 37 + bn_mp_dr_reduce.c | 88 + bn_mp_dr_setup.c | 26 + bn_mp_exch.c | 28 + bn_mp_expt_d.c | 51 + bn_mp_exptmod.c | 78 + bn_mp_exptmod_fast.c | 287 + bn_mp_exteuclid.c | 69 + bn_mp_fread.c | 61 + bn_mp_fwrite.c | 46 + bn_mp_gcd.c | 107 + bn_mp_get_int.c | 39 + bn_mp_grow.c | 51 + bn_mp_init.c | 33 + bn_mp_init_copy.c | 26 + bn_mp_init_multi.c | 53 + bn_mp_init_set.c | 26 + bn_mp_init_set_int.c | 25 + bn_mp_init_size.c | 33 + bn_mp_invmod.c | 174 + bn_mp_is_square.c | 103 + bn_mp_jacobi.c | 99 + bn_mp_karatsuba_mul.c | 164 + bn_mp_karatsuba_sqr.c | 115 + bn_mp_lcm.c | 54 + bn_mp_lshd.c | 61 + bn_mp_mod.c | 42 + bn_mp_mod_2d.c | 49 + bn_mp_mod_d.c | 21 + bn_mp_montgomery_calc_normalization.c | 53 + bn_mp_montgomery_reduce.c | 112 + bn_mp_montgomery_setup.c | 53 + bn_mp_mul.c | 48 + bn_mp_mul_2.c | 76 + bn_mp_mul_2d.c | 79 + bn_mp_mul_d.c | 72 + bn_mp_mulmod.c | 35 + bn_mp_n_root.c | 126 + bn_mp_neg.c | 28 + bn_mp_or.c | 44 + bn_mp_prime_fermat.c | 56 + bn_mp_prime_is_divisible.c | 44 + bn_mp_prime_is_prime.c | 77 + bn_mp_prime_miller_rabin.c | 97 + bn_mp_prime_next_prime.c | 164 + bn_mp_prime_random_ex.c | 118 + bn_mp_radix_size.c | 65 + bn_mp_radix_smap.c | 18 + bn_mp_rand.c | 49 + bn_mp_read_radix.c | 76 + bn_mp_read_signed_bin.c | 36 + bn_mp_read_unsigned_bin.c | 50 + bn_mp_reduce.c | 84 + bn_mp_reduce_2k.c | 56 + bn_mp_reduce_2k_setup.c | 42 + bn_mp_reduce_is_2k.c | 45 + bn_mp_reduce_setup.c | 29 + bn_mp_rshd.c | 66 + bn_mp_set.c | 23 + bn_mp_set_int.c | 42 + bn_mp_shrink.c | 29 + bn_mp_signed_bin_size.c | 21 + bn_mp_sqr.c | 41 + bn_mp_sqrmod.c | 35 + bn_mp_sqrt.c | 75 + bn_mp_sub.c | 53 + bn_mp_sub_d.c | 83 + bn_mp_submod.c | 36 + bn_mp_to_signed_bin.c | 28 + bn_mp_to_unsigned_bin.c | 43 + bn_mp_toom_mul.c | 272 + bn_mp_toom_sqr.c | 220 + bn_mp_toradix.c | 69 + bn_mp_toradix_n.c | 83 + bn_mp_unsigned_bin_size.c | 23 + bn_mp_xor.c | 45 + bn_mp_zero.c | 24 + bn_prime_sizes_tab.c | 51 + bn_prime_tab.c | 55 + bn_reverse.c | 33 + bn_s_mp_add.c | 103 + bn_s_mp_exptmod.c | 234 + bn_s_mp_mul_digs.c | 85 + bn_s_mp_mul_high_digs.c | 73 + bn_s_mp_sqr.c | 79 + bn_s_mp_sub.c | 83 + bncore.c | 31 + booker.pl | 261 + changes.txt | 304 + demo/demo.c | 785 +++ etc/2kprime.1 | 2 + etc/2kprime.c | 80 + etc/drprime.c | 60 + etc/drprimes.28 | 25 + etc/drprimes.txt | 6 + etc/makefile | 49 + etc/makefile.msvc | 23 + etc/mersenne.c | 140 + etc/mont.c | 46 + etc/pprime.c | 396 ++ etc/prime.1024 | 414 ++ etc/prime.512 | 205 + etc/timer.asm | 37 + etc/tune.c | 176 + gen.pl | 17 + logs/README | 13 + logs/add.log | 16 + logs/addsub.png | Bin 0 -> 7084 bytes logs/expt.log | 7 + logs/expt.png | Bin 0 -> 7439 bytes logs/expt_2k.log | 6 + logs/expt_dr.log | 7 + logs/graphs.dem | 17 + logs/index.html | 24 + logs/invmod.log | 32 + logs/invmod.png | Bin 0 -> 5735 bytes logs/k7/README | 13 + logs/k7/add.log | 16 + logs/k7/addsub.png | Bin 0 -> 6933 bytes logs/k7/expt.log | 7 + logs/k7/expt.png | Bin 0 -> 6490 bytes logs/k7/expt_dr.log | 7 + logs/k7/graphs.dem | 17 + logs/k7/index.html | 24 + logs/k7/invmod.log | 32 + logs/k7/invmod.png | Bin 0 -> 5730 bytes logs/k7/mult.log | 17 + logs/k7/mult.png | Bin 0 -> 8263 bytes logs/k7/mult_kara.log | 17 + logs/k7/sqr.log | 17 + logs/k7/sqr_kara.log | 17 + logs/k7/sub.log | 16 + logs/mult.log | 33 + logs/mult.png | Bin 0 -> 8100 bytes logs/mult_kara.log | 33 + logs/p4/README | 13 + logs/p4/add.log | 16 + logs/p4/addsub.png | Bin 0 -> 6898 bytes logs/p4/expt.log | 7 + logs/p4/expt.png | Bin 0 -> 6587 bytes logs/p4/expt_dr.log | 7 + logs/p4/graphs.dem | 17 + logs/p4/index.html | 24 + logs/p4/invmod.log | 32 + logs/p4/invmod.png | Bin 0 -> 5596 bytes logs/p4/mult.log | 17 + logs/p4/mult.png | Bin 0 -> 8100 bytes logs/p4/mult_kara.log | 17 + logs/p4/sqr.log | 17 + logs/p4/sqr_kara.log | 17 + logs/p4/sub.log | 16 + logs/sqr.log | 33 + logs/sqr.old | 17 + logs/sqr_kara.log | 33 + logs/sub.log | 16 + makefile | 121 + makefile.bcc | 41 + makefile.cygwin_dll | 48 + makefile.msvc | 35 + mtest/logtab.h | 20 + mtest/mpi-config.h | 86 + mtest/mpi-types.h | 16 + mtest/mpi.c | 3981 ++++++++++++ mtest/mpi.h | 227 + mtest/mtest.c | 303 + pics/design_process.sxd | Bin 0 -> 6950 bytes pics/design_process.tif | Bin 0 -> 79042 bytes pics/expt_state.sxd | Bin 0 -> 6869 bytes pics/expt_state.tif | Bin 0 -> 87542 bytes pics/makefile | 35 + pics/primality.tif | Bin 0 -> 85514 bytes pics/radix.sxd | Bin 0 -> 6181 bytes pics/sliding_window.sxd | Bin 0 -> 6787 bytes pics/sliding_window.tif | Bin 0 -> 53880 bytes pre_gen/mpi.c | 8414 +++++++++++++++++++++++++ tommath.h | 556 ++ 203 files changed, 25560 insertions(+) create mode 100644 LICENSE create mode 100644 bn_error.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_clear_multi.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_cnt_lsb.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_3.c create mode 100644 bn_mp_div_d.c create mode 100644 bn_mp_dr_is_modulus.c create mode 100644 bn_mp_dr_reduce.c create mode 100644 bn_mp_dr_setup.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_exteuclid.c create mode 100644 bn_mp_fread.c create mode 100644 bn_mp_fwrite.c create mode 100644 bn_mp_gcd.c create mode 100644 bn_mp_get_int.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_multi.c create mode 100644 bn_mp_init_set.c create mode 100644 bn_mp_init_set_int.c create mode 100644 bn_mp_init_size.c create mode 100644 bn_mp_invmod.c create mode 100644 bn_mp_is_square.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_calc_normalization.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_prime_fermat.c create mode 100644 bn_mp_prime_is_divisible.c create mode 100644 bn_mp_prime_is_prime.c create mode 100644 bn_mp_prime_miller_rabin.c create mode 100644 bn_mp_prime_next_prime.c create mode 100644 bn_mp_prime_random_ex.c create mode 100644 bn_mp_radix_size.c create mode 100644 bn_mp_radix_smap.c create mode 100644 bn_mp_rand.c create mode 100644 bn_mp_read_radix.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_reduce_2k.c create mode 100644 bn_mp_reduce_2k_setup.c create mode 100644 bn_mp_reduce_is_2k.c create mode 100644 bn_mp_reduce_setup.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_sqrt.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_toom_mul.c create mode 100644 bn_mp_toom_sqr.c create mode 100644 bn_mp_toradix.c create mode 100644 bn_mp_toradix_n.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_prime_sizes_tab.c create mode 100644 bn_prime_tab.c create mode 100644 bn_reverse.c create mode 100644 bn_s_mp_add.c create mode 100644 bn_s_mp_exptmod.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 create mode 100644 booker.pl create mode 100644 changes.txt create mode 100644 demo/demo.c create mode 100644 etc/2kprime.1 create mode 100644 etc/2kprime.c create mode 100644 etc/drprime.c create mode 100644 etc/drprimes.28 create mode 100644 etc/drprimes.txt create mode 100644 etc/makefile create mode 100644 etc/makefile.msvc create mode 100644 etc/mersenne.c create mode 100644 etc/mont.c create mode 100644 etc/pprime.c create mode 100644 etc/prime.1024 create mode 100644 etc/prime.512 create mode 100644 etc/timer.asm create mode 100644 etc/tune.c create mode 100644 gen.pl create mode 100644 logs/README create mode 100644 logs/add.log create mode 100644 logs/addsub.png create mode 100644 logs/expt.log create mode 100644 logs/expt.png create mode 100644 logs/expt_2k.log create mode 100644 logs/expt_dr.log create mode 100644 logs/graphs.dem create mode 100644 logs/index.html create mode 100644 logs/invmod.log create mode 100644 logs/invmod.png create mode 100644 logs/k7/README create mode 100644 logs/k7/add.log create mode 100644 logs/k7/addsub.png create mode 100644 logs/k7/expt.log create mode 100644 logs/k7/expt.png create mode 100644 logs/k7/expt_dr.log create mode 100644 logs/k7/graphs.dem create mode 100644 logs/k7/index.html create mode 100644 logs/k7/invmod.log create mode 100644 logs/k7/invmod.png create mode 100644 logs/k7/mult.log create mode 100644 logs/k7/mult.png create mode 100644 logs/k7/mult_kara.log create mode 100644 logs/k7/sqr.log create mode 100644 logs/k7/sqr_kara.log create mode 100644 logs/k7/sub.log create mode 100644 logs/mult.log create mode 100644 logs/mult.png create mode 100644 logs/mult_kara.log create mode 100644 logs/p4/README create mode 100644 logs/p4/add.log create mode 100644 logs/p4/addsub.png create mode 100644 logs/p4/expt.log create mode 100644 logs/p4/expt.png create mode 100644 logs/p4/expt_dr.log create mode 100644 logs/p4/graphs.dem create mode 100644 logs/p4/index.html create mode 100644 logs/p4/invmod.log create mode 100644 logs/p4/invmod.png create mode 100644 logs/p4/mult.log create mode 100644 logs/p4/mult.png create mode 100644 logs/p4/mult_kara.log create mode 100644 logs/p4/sqr.log create mode 100644 logs/p4/sqr_kara.log create mode 100644 logs/p4/sub.log create mode 100644 logs/sqr.log create mode 100644 logs/sqr.old create mode 100644 logs/sqr_kara.log create mode 100644 logs/sub.log create mode 100644 makefile create mode 100644 makefile.bcc create mode 100644 makefile.cygwin_dll create mode 100644 makefile.msvc create mode 100644 mtest/logtab.h create mode 100644 mtest/mpi-config.h create mode 100644 mtest/mpi-types.h create mode 100644 mtest/mpi.c create mode 100644 mtest/mpi.h create mode 100644 mtest/mtest.c create mode 100644 pics/design_process.sxd create mode 100644 pics/design_process.tif create mode 100644 pics/expt_state.sxd create mode 100644 pics/expt_state.tif create mode 100644 pics/makefile create mode 100644 pics/primality.tif create mode 100644 pics/radix.sxd create mode 100644 pics/sliding_window.sxd create mode 100644 pics/sliding_window.tif create mode 100644 pre_gen/mpi.c create mode 100644 tommath.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5baa792 --- /dev/null +++ b/LICENSE @@ -0,0 +1,4 @@ +LibTomMath is hereby released into the Public Domain. + +-- Tom St Denis + diff --git a/bn_error.c b/bn_error.c new file mode 100644 index 0000000..7cac649 --- /dev/null +++ b/bn_error.c @@ -0,0 +1,41 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +static const struct { + int code; + char *msg; +} msgs[] = { + { MP_OKAY, "Successful" }, + { MP_MEM, "Out of heap" }, + { MP_VAL, "Value out of range" } +}; + +/* return a char * string for a given code */ +char *mp_error_to_string(int code) +{ + int x; + + /* scan the lookup table for the given message */ + for (x = 0; x < (int)(sizeof(msgs) / sizeof(msgs[0])); x++) { + if (msgs[x].code == code) { + return msgs[x].msg; + } + } + + /* generic reply for invalid code */ + return "Invalid error code"; +} + diff --git a/bn_fast_mp_invmod.c b/bn_fast_mp_invmod.c new file mode 100644 index 0000000..c9a9459 --- /dev/null +++ b/bn_fast_mp_invmod.c @@ -0,0 +1,143 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes the modular inverse via binary extended euclidean algorithm, + * that is c = 1/a mod b + * + * Based on mp_invmod except this is optimized for the case where b is + * odd as per HAC Note 14.64 on pp. 610 + */ +int +fast_mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, B, D; + int res, neg; + + /* 2. [modified] b must be odd */ + if (mp_iseven (b) == 1) { + return MP_VAL; + } + + /* init all our temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x == modulus, y == value to invert */ + if ((res = mp_copy (b, &x)) != MP_OKAY) { + goto __ERR; + } + + /* we need y = |a| */ + if ((res = mp_abs (a, &y)) != MP_OKAY) { + goto __ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto __ERR; + } + 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 __ERR; + } + /* 4.2 if B is odd then */ + if (mp_isodd (&B) == 1) { + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto __ERR; + } + } + /* B = B/2 */ + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + /* 5.2 if D is odd then */ + if (mp_isodd (&D) == 1) { + /* D = (D-x)/2 */ + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto __ERR; + } + } + /* D = D/2 */ + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto __ERR; + } + } else { + /* v - v - u, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + + /* b is now the inverse */ + neg = a->sign; + while (D.sign == MP_NEG) { + if ((res = mp_add (&D, b, &D)) != MP_OKAY) { + goto __ERR; + } + } + mp_exch (&D, c); + c->sign = neg; + res = MP_OKAY; + +__ERR:mp_clear_multi (&x, &y, &u, &v, &B, &D, NULL); + return res; +} diff --git a/bn_fast_mp_montgomery_reduce.c b/bn_fast_mp_montgomery_reduce.c new file mode 100644 index 0000000..c7a3413 --- /dev/null +++ b/bn_fast_mp_montgomery_reduce.c @@ -0,0 +1,167 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of mp_montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int +fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = 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 mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * 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 *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((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 + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + 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..75fa706 --- /dev/null +++ b/bn_fast_s_mp_mul_digs.c @@ -0,0 +1,130 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +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[MP_WARRAY]; + + /* grow the destination as required */ + 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; + + { + register mp_digit *tmpc; + + /* 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. + * + * In this particular implementation we feed the carries from + * behind which means when the loop terminates we still have one + * last digit to copy + */ + tmpc = c->dp; + for (ix = 1; ix < digs; ix++) { + /* forward the carry from the previous temp */ + W[ix] += (W[ix - 1] >> ((mp_word) DIGIT_BIT)); + + /* now extract the previous digit [below the carry] */ + *tmpc++ = (mp_digit) (W[ix - 1] & ((mp_word) MP_MASK)); + } + /* fetch the last digit */ + *tmpc++ = (mp_digit) (W[digs - 1] & ((mp_word) MP_MASK)); + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 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..779aa14 --- /dev/null +++ b/bn_fast_s_mp_mul_high_digs.c @@ -0,0 +1,98 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ + #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. + * + * Based on Algorithm 14.12 on pp.595 of HAC. + */ +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[MP_WARRAY]; + + /* calculate size of product and allocate more space if required */ + 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; + + /* skip cases below zero where ix > digs */ + if (iy < 0) { + iy = abs(iy); + tmpy += iy; + _W += iy; + iy = 0; + } + + /* 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 + * + * See comments in bn_fast_s_mp_mul_digs.c + */ + 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[newused - 1] = (mp_digit) (W[newused - 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..f62ae54 --- /dev/null +++ b/bn_fast_s_mp_sqr.c @@ -0,0 +1,139 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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! + * + * Based on Algorithm 14.16 on pp.597 of HAC. + * + */ +int fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, newused, res, ix, pa; + mp_word W2[MP_WARRAY], W[MP_WARRAY]; + + /* calculate size of product and allocate as required */ + 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 outer 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 + * + * Note that every outer product is computed + * for a particular column only once which means that + * there is no need todo a double precision addition + * into the W2[] array. + */ + 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; + + /* now compute digits + * + * We have to double the inner product sums, add in the + * outer product sums, propagate carries and convert + * to single precision. + */ + { + register mp_digit *tmpb; + + /* double first value, since the inner products are + * half of what they should be + */ + W[0] += W[0] + W2[0]; + + tmpb = b->dp; + for (ix = 1; ix < newused; ix++) { + /* double/add next digit */ + W[ix] += W[ix] + W2[ix]; + + /* propagate carry forwards [from the previous digit] */ + W[ix] = W[ix] + (W[ix - 1] >> ((mp_word) DIGIT_BIT)); + + /* store the current digit now that the carry isn't + * needed + */ + *tmpb++ = (mp_digit) (W[ix - 1] & ((mp_word) MP_MASK)); + } + /* set the last value. Note even if the carry is zero + * this is required since the next step will not zero + * it if b originally had a value at b->dp[2*a.used] + */ + *tmpb++ = (mp_digit) (W[(newused) - 1] & ((mp_word) MP_MASK)); + + /* clear high digits of b if there were any originally */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + + mp_clamp (b); + return MP_OKAY; +} diff --git a/bn_mp_2expt.c b/bn_mp_2expt.c new file mode 100644 index 0000000..5c4e256 --- /dev/null +++ b/bn_mp_2expt.c @@ -0,0 +1,42 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +int +mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accomodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + 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..a5e9ab0 --- /dev/null +++ b/bn_mp_abs.c @@ -0,0 +1,37 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +int +mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + 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..f625195 --- /dev/null +++ b/bn_mp_add.c @@ -0,0 +1,47 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* high level addition (handles signs) */ +int mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + diff --git a/bn_mp_add_d.c b/bn_mp_add_d.c new file mode 100644 index 0000000..edc93c1 --- /dev/null +++ b/bn_mp_add_d.c @@ -0,0 +1,103 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* single digit addition */ +int +mp_add_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, ix, oldused; + mp_digit *tmpa, *tmpc, mu; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative and |a| >= b, call c = |a| - b */ + if (a->sign == MP_NEG && (a->used > 1 || a->dp[0] >= b)) { + /* temporarily fix sign of a */ + a->sign = MP_ZPOS; + + /* c = |a| - b */ + res = mp_sub_d(a, b, c); + + /* fix sign */ + a->sign = c->sign = MP_NEG; + + return res; + } + + /* old number of used digits in c */ + oldused = c->used; + + /* sign always positive */ + c->sign = MP_ZPOS; + + /* source alias */ + tmpa = a->dp; + + /* destination alias */ + tmpc = c->dp; + + /* if a is positive */ + if (a->sign == MP_ZPOS) { + /* add digit, after this we're propagating + * the carry. + */ + *tmpc = *tmpa++ + b; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + + /* now handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ + mu; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + } + /* set final carry */ + ix++; + *tmpc++ = mu; + + /* setup size */ + c->used = a->used + 1; + } else { + /* a was negative and |a| < b */ + c->used = 1; + + /* the result is a single digit */ + if (a->used == 1) { + *tmpc++ = b - a->dp[0]; + } else { + *tmpc++ = b; + } + + /* setup count so the clearing of oldused + * can fall through correctly + */ + ix = 1; + } + + /* now zero to oldused */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + + return MP_OKAY; +} + diff --git a/bn_mp_addmod.c b/bn_mp_addmod.c new file mode 100644 index 0000000..691dc50 --- /dev/null +++ b/bn_mp_addmod.c @@ -0,0 +1,35 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..9abb618 --- /dev/null +++ b/bn_mp_and.c @@ -0,0 +1,51 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..c6285ce --- /dev/null +++ b/bn_mp_clamp.c @@ -0,0 +1,38 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +void +mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + 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..afe9b26 --- /dev/null +++ b/bn_mp_clear.c @@ -0,0 +1,34 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* clear one (frees) */ +void +mp_clear (mp_int * a) +{ + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + memset (a->dp, 0, sizeof (mp_digit) * a->used); + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} diff --git a/bn_mp_clear_multi.c b/bn_mp_clear_multi.c new file mode 100644 index 0000000..d262b2f --- /dev/null +++ b/bn_mp_clear_multi.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include +#include + +void mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} diff --git a/bn_mp_cmp.c b/bn_mp_cmp.c new file mode 100644 index 0000000..ed27d21 --- /dev/null +++ b/bn_mp_cmp.c @@ -0,0 +1,37 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* compare two ints (signed)*/ +int +mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} diff --git a/bn_mp_cmp_d.c b/bn_mp_cmp_d.c new file mode 100644 index 0000000..6baa172 --- /dev/null +++ b/bn_mp_cmp_d.c @@ -0,0 +1,38 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* compare a digit */ +int mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + 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..b50f769 --- /dev/null +++ b/bn_mp_cmp_mag.c @@ -0,0 +1,49 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* compare maginitude of two ints (unsigned) */ +int mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} diff --git a/bn_mp_cnt_lsb.c b/bn_mp_cnt_lsb.c new file mode 100644 index 0000000..07cb709 --- /dev/null +++ b/bn_mp_cnt_lsb.c @@ -0,0 +1,47 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +static const int lnz[16] = { + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a) +{ + int x; + mp_digit q, qq; + + /* easy out */ + if (mp_iszero(a) == 1) { + return 0; + } + + /* scan lower digits until non-zero */ + for (x = 0; x < a->used && a->dp[x] == 0; x++); + q = a->dp[x]; + x *= DIGIT_BIT; + + /* now scan this digit until a 1 is found */ + if ((q & 1) == 0) { + do { + qq = q & 15; + x += lnz[qq]; + q >>= 4; + } while (qq == 0); + } + return x; +} + diff --git a/bn_mp_copy.c b/bn_mp_copy.c new file mode 100644 index 0000000..1c45f3e --- /dev/null +++ b/bn_mp_copy.c @@ -0,0 +1,62 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* copy, b = a */ +int +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} diff --git a/bn_mp_count_bits.c b/bn_mp_count_bits.c new file mode 100644 index 0000000..33b971b --- /dev/null +++ b/bn_mp_count_bits.c @@ -0,0 +1,39 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* returns the number of bits in an int */ +int +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + 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..652a094 --- /dev/null +++ b/bn_mp_div.c @@ -0,0 +1,211 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto __Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto __Y; + } + } else { + norm = 0; + } + + /* 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; + + /* 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.used) { + 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] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } 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)); + } + + /* 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_mag(&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; + } + + /* 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_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..ebad7ae --- /dev/null +++ b/bn_mp_div_2.c @@ -0,0 +1,62 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = a/2 */ +int mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + 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..75f090c --- /dev/null +++ b/bn_mp_div_2d.c @@ -0,0 +1,91 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* shift right by a certain bit count (store quotient in c, optional 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 */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} diff --git a/bn_mp_div_3.c b/bn_mp_div_3.c new file mode 100644 index 0000000..3bc2bd2 --- /dev/null +++ b/bn_mp_div_3.c @@ -0,0 +1,73 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* divide by three (based on routine from MPI and the GMP manual) */ +int +mp_div_3 (mp_int * a, mp_int *c, mp_digit * d) +{ + mp_int q; + mp_word w, t; + mp_digit b; + int res, ix; + + /* b = 2**DIGIT_BIT / 3 */ + b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); + + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= 3) { + /* multiply w by [1/3] */ + t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); + + /* now subtract 3 * [w/3] from w, to get the remainder */ + w -= t+t+t; + + /* fixup the remainder as required since + * the optimization is not exact. + */ + while (w >= 3) { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + /* [optional] store the remainder */ + if (d != NULL) { + *d = (mp_digit)w; + } + + /* [optional] store the quotient */ + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + diff --git a/bn_mp_div_d.c b/bn_mp_div_d.c new file mode 100644 index 0000000..f8caf83 --- /dev/null +++ b/bn_mp_div_d.c @@ -0,0 +1,102 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +static int s_is_power_of_two(mp_digit b, int *p) +{ + int x; + + for (x = 1; x < DIGIT_BIT; x++) { + if (b == (((mp_digit)1)<dp[0] & ((1<used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= b) { + t = (mp_digit)(w / b); + w -= ((mp_word)t) * ((mp_word)b); + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + if (d != NULL) { + *d = (mp_digit)w; + } + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + diff --git a/bn_mp_dr_is_modulus.c b/bn_mp_dr_is_modulus.c new file mode 100644 index 0000000..0206e0e --- /dev/null +++ b/bn_mp_dr_is_modulus.c @@ -0,0 +1,37 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines if a number is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a) +{ + int ix; + + /* must be at least two digits */ + if (a->used < 2) { + return 0; + } + + /* must be of the form b**k - a [a <= b] so all + * but the first digit must be equal to -1 (mod b). + */ + for (ix = 1; ix < a->used; ix++) { + if (a->dp[ix] != MP_MASK) { + return 0; + } + } + return 1; +} + diff --git a/bn_mp_dr_reduce.c b/bn_mp_dr_reduce.c new file mode 100644 index 0000000..bad240a --- /dev/null +++ b/bn_mp_dr_reduce.c @@ -0,0 +1,88 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* reduce "x" in place modulo "n" using the Diminished Radix algorithm. + * + * Based on algorithm from the paper + * + * "Generating Efficient Primes for Discrete Log Cryptosystems" + * Chae Hoon Lim, Pil Loong Lee, + * POSTECH Information Research Laboratories + * + * The modulus must be of a special format [see manual] + * + * Has been modified to use algorithm 7.10 from the LTM book instead + * + * Input x must be in the range 0 <= x <= (n-1)**2 + */ +int +mp_dr_reduce (mp_int * x, mp_int * n, mp_digit k) +{ + int err, i, m; + mp_word r; + mp_digit mu, *tmpx1, *tmpx2; + + /* m = digits in modulus */ + m = n->used; + + /* ensure that "x" has at least 2m digits */ + if (x->alloc < m + m) { + if ((err = mp_grow (x, m + m)) != MP_OKAY) { + return err; + } + } + +/* top of loop, this is where the code resumes if + * another reduction pass is required. + */ +top: + /* aliases for digits */ + /* alias for lower half of x */ + tmpx1 = x->dp; + + /* alias for upper half of x, or x/B**m */ + tmpx2 = x->dp + m; + + /* set carry to zero */ + mu = 0; + + /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ + for (i = 0; i < m; i++) { + r = ((mp_word)*tmpx2++) * ((mp_word)k) + *tmpx1 + mu; + *tmpx1++ = (mp_digit)(r & MP_MASK); + mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* set final carry */ + *tmpx1++ = mu; + + /* zero words above m */ + for (i = m + 1; i < x->used; i++) { + *tmpx1++ = 0; + } + + /* clamp, sub and return */ + mp_clamp (x); + + /* if x >= n then subtract and reduce again + * Each successive "recursion" makes the input smaller and smaller. + */ + if (mp_cmp_mag (x, n) != MP_LT) { + s_mp_sub(x, n, x); + goto top; + } + return MP_OKAY; +} diff --git a/bn_mp_dr_setup.c b/bn_mp_dr_setup.c new file mode 100644 index 0000000..f269436 --- /dev/null +++ b/bn_mp_dr_setup.c @@ -0,0 +1,26 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines the setup value */ +void mp_dr_setup(mp_int *a, mp_digit *d) +{ + /* the casts are required if DIGIT_BIT is one less than + * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] + */ + *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - + ((mp_word)a->dp[0])); +} + diff --git a/bn_mp_exch.c b/bn_mp_exch.c new file mode 100644 index 0000000..b0f1cd0 --- /dev/null +++ b/bn_mp_exch.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +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..6ec12a4 --- /dev/null +++ b/bn_mp_expt_d.c @@ -0,0 +1,51 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* calculate c = a**b using a square-multiply algorithm */ +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++) { + /* square */ + if ((res = mp_sqr (c, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + + /* if the bit is set multiply */ + if ((b & (mp_digit) (((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { + if ((res = mp_mul (c, &g, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + } + + /* shift to next bit */ + 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..21d0cc5 --- /dev/null +++ b/bn_mp_exptmod.c @@ -0,0 +1,78 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); + + /* if not, is it a uDR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } + + /* if the modulus is odd or dr != 0 use the fast method */ + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y); + } +} + diff --git a/bn_mp_exptmod_fast.c b/bn_mp_exptmod_fast.c new file mode 100644 index 0000000..c281733 --- /dev/null +++ b/bn_mp_exptmod_fast.c @@ -0,0 +1,287 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 or Diminished Radix reduction [whichever appropriate] + */ + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int +mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* 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; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto __M; + } + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else { + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; + } + } else if (redmode == 1) { + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; + } else { + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto __M; + } + redux = mp_reduce_2k; + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto __M; + } + + /* 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 (redmode == 0) { + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto __RES; + } + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto __RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, 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 = redux (&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 = redux (&M[x], P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)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 = redux (&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 */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto __RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + + /* empty window and reset */ + bitcpy = 0; + 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 = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto __RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +__RES:mp_clear (&res); +__M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} diff --git a/bn_mp_exteuclid.c b/bn_mp_exteuclid.c new file mode 100644 index 0000000..cb3f787 --- /dev/null +++ b/bn_mp_exteuclid.c @@ -0,0 +1,69 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Extended euclidean algorithm of (a, b) produces + a*u1 + b*u2 = u3 + */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) +{ + mp_int u1,u2,u3,v1,v2,v3,t1,t2,t3,q,tmp; + int err; + + if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { + return err; + } + + /* initialize, (u1,u2,u3) = (1,0,a) */ + mp_set(&u1, 1); + if ((err = mp_copy(a, &u3)) != MP_OKAY) { goto _ERR; } + + /* initialize, (v1,v2,v3) = (0,1,b) */ + mp_set(&v2, 1); + if ((err = mp_copy(b, &v3)) != MP_OKAY) { goto _ERR; } + + /* loop while v3 != 0 */ + while (mp_iszero(&v3) == MP_NO) { + /* q = u3/v3 */ + if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { goto _ERR; } + + /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ + if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { goto _ERR; } + + /* (u1,u2,u3) = (v1,v2,v3) */ + if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { goto _ERR; } + + /* (v1,v2,v3) = (t1,t2,t3) */ + if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { goto _ERR; } + } + + /* copy result out */ + if (U1 != NULL) { mp_exch(U1, &u1); } + if (U2 != NULL) { mp_exch(U2, &u2); } + if (U3 != NULL) { mp_exch(U3, &u3); } + + err = MP_OKAY; +_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); + return err; +} diff --git a/bn_mp_fread.c b/bn_mp_fread.c new file mode 100644 index 0000000..ef665e5 --- /dev/null +++ b/bn_mp_fread.c @@ -0,0 +1,61 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* read a bigint from a file stream in ASCII */ +int mp_fread(mp_int *a, int radix, FILE *stream) +{ + int err, ch, neg, y; + + /* clear a */ + mp_zero(a); + + /* if first digit is - then set negative */ + ch = fgetc(stream); + if (ch == '-') { + neg = MP_NEG; + ch = fgetc(stream); + } else { + neg = MP_ZPOS; + } + + for (;;) { + /* find y in the radix map */ + for (y = 0; y < radix; y++) { + if (mp_s_rmap[y] == ch) { + break; + } + } + if (y == radix) { + break; + } + + /* shift up and add */ + if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + + ch = fgetc(stream); + } + if (mp_cmp_d(a, 0) != MP_EQ) { + a->sign = neg; + } + + return MP_OKAY; +} + diff --git a/bn_mp_fwrite.c b/bn_mp_fwrite.c new file mode 100644 index 0000000..2853ec1 --- /dev/null +++ b/bn_mp_fwrite.c @@ -0,0 +1,46 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +int mp_fwrite(mp_int *a, int radix, FILE *stream) +{ + char *buf; + int err, len, x; + + if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { + return err; + } + + buf = OPT_CAST(char) XMALLOC (len); + if (buf == NULL) { + return MP_MEM; + } + + if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { + XFREE (buf); + return err; + } + + for (x = 0; x < len; x++) { + if (fputc(buf[x], stream) == EOF) { + XFREE (buf); + return MP_VAL; + } + } + + XFREE (buf); + return MP_OKAY; +} + diff --git a/bn_mp_gcd.c b/bn_mp_gcd.c new file mode 100644 index 0000000..78ddda2 --- /dev/null +++ b/bn_mp_gcd.c @@ -0,0 +1,107 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Greatest Common Divisor using the binary method */ +int mp_gcd (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int u, v; + int k, u_lsb, v_lsb, res; + + /* either zero than gcd is the largest */ + if (mp_iszero (a) == 1 && mp_iszero (b) == 0) { + return mp_abs (b, c); + } + if (mp_iszero (a) == 0 && mp_iszero (b) == 1) { + return mp_abs (a, c); + } + + /* optimized. At this point if a == 0 then + * b must equal zero too + */ + if (mp_iszero (a) == 1) { + mp_zero(c); + return MP_OKAY; + } + + /* get copies of a and b we can modify */ + 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; + + /* B1. Find the common power of two for u and v */ + u_lsb = mp_cnt_lsb(&u); + v_lsb = mp_cnt_lsb(&v); + k = MIN(u_lsb, v_lsb); + + if (k > 0) { + /* divide the power of two out */ + if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { + goto __V; + } + + if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { + goto __V; + } + } + + /* divide any remaining factors of two out */ + if (u_lsb != k) { + if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { + goto __V; + } + } + + if (v_lsb != k) { + if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { + goto __V; + } + } + + while (mp_iszero(&v) == 0) { + /* make sure v is the largest */ + if (mp_cmp_mag(&u, &v) == MP_GT) { + /* swap u and v to make sure v is >= u */ + mp_exch(&u, &v); + } + + /* subtract smallest from largest */ + if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { + goto __V; + } + + /* Divide out all factors of two */ + if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { + goto __V; + } + } + + /* multiply by 2**k which we divided out at the beginning */ + if ((res = mp_mul_2d (&u, k, c)) != MP_OKAY) { + goto __V; + } + c->sign = MP_ZPOS; + res = MP_OKAY; +__V:mp_clear (&u); +__U:mp_clear (&v); + return res; +} diff --git a/bn_mp_get_int.c b/bn_mp_get_int.c new file mode 100644 index 0000000..41df6e1 --- /dev/null +++ b/bn_mp_get_int.c @@ -0,0 +1,39 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* get the lower 32-bits of an mp_int */ +unsigned long mp_get_int(mp_int * a) +{ + int i; + unsigned long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used,(int)((sizeof(unsigned long)*CHAR_BIT+DIGIT_BIT-1)/DIGIT_BIT))-1; + + /* get most significant digit of result */ + res = DIGIT(a,i); + + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a,i); + } + + /* force result to 32-bits always so it is consistent on non 32-bit platforms */ + return res & 0xFFFFFFFFUL; +} diff --git a/bn_mp_grow.c b/bn_mp_grow.c new file mode 100644 index 0000000..43a3028 --- /dev/null +++ b/bn_mp_grow.c @@ -0,0 +1,51 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* grow as required */ +int mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; 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..5c5c1ad --- /dev/null +++ b/bn_mp_init.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* init a new bigint */ +int mp_init (mp_int * a) +{ + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XCALLOC (sizeof (mp_digit), MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the used to zero, allocated digits 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..1c5da4d --- /dev/null +++ b/bn_mp_init_copy.c @@ -0,0 +1,26 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + } + return mp_copy (b, a); +} diff --git a/bn_mp_init_multi.c b/bn_mp_init_multi.c new file mode 100644 index 0000000..99ce331 --- /dev/null +++ b/bn_mp_init_multi.c @@ -0,0 +1,53 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include +#include + +int mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} + diff --git a/bn_mp_init_set.c b/bn_mp_init_set.c new file mode 100644 index 0000000..c8d8bf8 --- /dev/null +++ b/bn_mp_init_set.c @@ -0,0 +1,26 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + mp_set(a, b); + return err; +} diff --git a/bn_mp_init_set_int.c b/bn_mp_init_set_int.c new file mode 100644 index 0000000..2d6628d --- /dev/null +++ b/bn_mp_init_set_int.c @@ -0,0 +1,25 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* initialize and set a digit */ +int mp_init_set_int (mp_int * a, unsigned long b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + return mp_set_int(a, b); +} diff --git a/bn_mp_init_size.c b/bn_mp_init_size.c new file mode 100644 index 0000000..c763ee0 --- /dev/null +++ b/bn_mp_init_size.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* init an mp_init for a given size */ +int mp_init_size (mp_int * a, int size) +{ + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XCALLOC (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..4ac5b2f --- /dev/null +++ b/bn_mp_invmod.c @@ -0,0 +1,174 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* hac 14.61, pp608 */ +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 || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_copy (a, &x)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto __ERR; + } + + /* 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 __ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto __ERR; + } + 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 __ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto __ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto __ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto __ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto __ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto __ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +__ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} diff --git a/bn_mp_is_square.c b/bn_mp_is_square.c new file mode 100644 index 0000000..1f01bca --- /dev/null +++ b/bn_mp_is_square.c @@ -0,0 +1,103 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Check if remainders are possible squares - fast exclude non-squares */ +static const char rem_128[128] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 +}; + +static const char rem_105[105] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 +}; + +/* Store non-zero to ret if arg is square, and zero if not */ +int mp_is_square(mp_int *arg,int *ret) +{ + int res; + mp_digit c; + mp_int t; + unsigned long r; + + /* Default to Non-square :) */ + *ret = MP_NO; + + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* digits used? (TSD) */ + if (arg->used == 0) { + return MP_OKAY; + } + + /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ + if (rem_128[127 & DIGIT(arg,0)] == 1) { + return MP_OKAY; + } + + /* Next check mod 105 (3*5*7) */ + if ((res = mp_mod_d(arg,105,&c)) != MP_OKAY) { + return res; + } + if (rem_105[c] == 1) { + return MP_OKAY; + } + + /* product of primes less than 2^31 */ + if ((res = mp_init_set_int(&t,11L*13L*17L*19L*23L*29L*31L)) != MP_OKAY) { + return res; + } + if ((res = mp_mod(arg,&t,&t)) != MP_OKAY) { + goto ERR; + } + r = mp_get_int(&t); + /* Check for other prime modules, note it's not an ERROR but we must + * free "t" so the easiest way is to goto ERR. We know that res + * is already equal to MP_OKAY from the mp_mod call + */ + if ( (1L<<(r%11)) & 0x5C4L ) goto ERR; + if ( (1L<<(r%13)) & 0x9E4L ) goto ERR; + if ( (1L<<(r%17)) & 0x5CE8L ) goto ERR; + if ( (1L<<(r%19)) & 0x4F50CL ) goto ERR; + if ( (1L<<(r%23)) & 0x7ACCA0L ) goto ERR; + if ( (1L<<(r%29)) & 0xC2EDD0CL ) goto ERR; + if ( (1L<<(r%31)) & 0x6DE2B848L ) goto ERR; + + /* Final check - is sqr(sqrt(arg)) == arg ? */ + if ((res = mp_sqrt(arg,&t)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&t,&t)) != MP_OKAY) { + goto ERR; + } + + *ret = (mp_cmp_mag(&t,arg) == MP_EQ) ? MP_YES : MP_NO; +ERR:mp_clear(&t); + return res; +} diff --git a/bn_mp_jacobi.c b/bn_mp_jacobi.c new file mode 100644 index 0000000..634370f --- /dev/null +++ b/bn_mp_jacobi.c @@ -0,0 +1,99 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes the jacobi c = (a | n) (or Legendre if n is prime) + * HAC pp. 73 Algorithm 2.149 + */ +int mp_jacobi (mp_int * a, mp_int * p, int *c) +{ + mp_int a1, p1; + int k, s, r, res; + mp_digit residue; + + /* if p <= 0 return MP_VAL */ + if (mp_cmp_d(p, 0) != MP_GT) { + return MP_VAL; + } + + /* 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**k */ + if ((res = mp_init_copy (&a1, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&p1)) != MP_OKAY) { + goto __A1; + } + + /* divide out larger power of two */ + k = mp_cnt_lsb(&a1); + if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { + goto __P1; + } + + /* step 4. if e is even set s=1 */ + if ((k & 1) == 0) { + s = 1; + } else { + /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ + residue = p->dp[0] & 7; + + if (residue == 1 || residue == 7) { + s = 1; + } else if (residue == 3 || residue == 5) { + s = -1; + } + } + + /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ + if ( ((p->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 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 (p, &a1, &p1)) != MP_OKAY) { + goto __P1; + } + if ((res = mp_jacobi (&p1, &a1, &r)) != MP_OKAY) { + goto __P1; + } + *c = s * r; + } + + /* done */ + res = MP_OKAY; +__P1:mp_clear (&p1); +__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..169dacf --- /dev/null +++ b/bn_mp_karatsuba_mul.c @@ -0,0 +1,164 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 - a0)(b1 - 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, x0y0, x1y1; + int B, err; + + /* default the return code to an error */ + err = MP_MEM; + + /* min # of digits */ + B = MIN (a->used, b->used); + + /* now divide in two */ + B = B >> 1; + + /* 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_size (&t1, B * 2) != MP_OKAY) + goto Y1; + if (mp_init_size (&x0y0, B * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x1y1, B * 2) != 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; + + { + register int x; + register mp_digit *tmpa, *tmpb, *tmpx, *tmpy; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + tmpa = a->dp; + tmpb = b->dp; + + tmpx = x0.dp; + tmpy = y0.dp; + for (x = 0; x < B; x++) { + *tmpx++ = *tmpa++; + *tmpy++ = *tmpb++; + } + + tmpx = x1.dp; + for (x = B; x < a->used; x++) { + *tmpx++ = *tmpa++; + } + + tmpy = y1.dp; + for (x = B; x < b->used; x++) { + *tmpy++ = *tmpb++; + } + } + + /* 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 */ + /* after this x0 is no longer required, free temp [x0==t2]! */ + 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, &x0) != MP_OKAY) + goto X1Y1; /* t2 = y1 - y0 */ + if (mp_mul (&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1 - x0) * (y1 - y0) */ + + /* add x0y0 */ + if (mp_add (&x0y0, &x1y1, &x0) != MP_OKAY) + goto X1Y1; /* t2 = x0y0 + x1y1 */ + if (mp_sub (&x0, &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; + + err = MP_MEM; + + /* min # of digits */ + B = a->used; + + /* now divide in two */ + B = B >> 1; + + /* 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_size (&t1, a->used * 2) != MP_OKAY) + goto X1; + if (mp_init_size (&t2, a->used * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x0x0, B * 2) != MP_OKAY) + goto T2; + if (mp_init_size (&x1x1, (a->used - B) * 2) != MP_OKAY) + goto X0X0; + + { + register int x; + register mp_digit *dst, *src; + + src = a->dp; + + /* now shift the digits */ + dst = x0.dp; + for (x = 0; x < B; x++) { + *dst++ = *src++; + } + + dst = x1.dp; + for (x = B; x < a->used; x++) { + *dst++ = *src++; + } + } + + 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)**2 */ + 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) * (x1 - x0) */ + + /* add x0y0 */ + if (s_mp_add (&x0x0, &x1x1, &t2) != MP_OKAY) + goto X1X1; /* t2 = x0x0 + x1x1 */ + if (mp_sub (&t2, &t1, &t1) != MP_OKAY) + goto X1X1; /* t1 = x0x0 + x1x1 - (x1-x0)*(x1-x0) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))< + +/* 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 t1, t2; + + + if ((res = mp_init_multi (&t1, &t2, NULL)) != MP_OKAY) { + return res; + } + + /* t1 = get the GCD of the two inputs */ + if ((res = mp_gcd (a, b, &t1)) != MP_OKAY) { + goto __T; + } + + /* divide the smallest by the GCD */ + if (mp_cmp_mag(a, b) == MP_LT) { + /* store quotient in t2 such that t2 * b is the LCM */ + if ((res = mp_div(a, &t1, &t2, NULL)) != MP_OKAY) { + goto __T; + } + res = mp_mul(b, &t2, c); + } else { + /* store quotient in t2 such that t2 * a is the LCM */ + if ((res = mp_div(b, &t1, &t2, NULL)) != MP_OKAY) { + goto __T; + } + res = mp_mul(a, &t2, c); + } + + /* fix the sign to positive */ + c->sign = MP_ZPOS; + +__T: + mp_clear_multi (&t1, &t2, NULL); + return res; +} diff --git a/bn_mp_lshd.c b/bn_mp_lshd.c new file mode 100644 index 0000000..f170237 --- /dev/null +++ b/bn_mp_lshd.c @@ -0,0 +1,61 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} diff --git a/bn_mp_mod.c b/bn_mp_mod.c new file mode 100644 index 0000000..ad963a9 --- /dev/null +++ b/bn_mp_mod.c @@ -0,0 +1,42 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 != b->sign) { + 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..89e9081 --- /dev/null +++ b/bn_mp_mod_2d.c @@ -0,0 +1,49 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..c5dbe46 --- /dev/null +++ b/bn_mp_mod_d.c @@ -0,0 +1,21 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +int +mp_mod_d (mp_int * a, mp_digit b, mp_digit * c) +{ + return mp_div_d(a, b, NULL, c); +} diff --git a/bn_mp_montgomery_calc_normalization.c b/bn_mp_montgomery_calc_normalization.c new file mode 100644 index 0000000..a38555c --- /dev/null +++ b/bn_mp_montgomery_calc_normalization.c @@ -0,0 +1,53 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* calculates a = B^n mod b for Montgomery reduction + * Where B is the base [e.g. 2^DIGIT_BIT]. + * B^n mod b is computed by first computing + * A = B^(n-1) which doesn't require a reduction but a simple OR. + * then C = A * B = B^n is computed by performing upto DIGIT_BIT + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b. This saves alot of multiple precision shifting. + */ +int +mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + /* compute A = B^(n-1) * 2^(bits-1) */ + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} diff --git a/bn_mp_montgomery_reduce.c b/bn_mp_montgomery_reduce.c new file mode 100644 index 0000000..52b98cd --- /dev/null +++ b/bn_mp_montgomery_reduce.c @@ -0,0 +1,112 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, digs; + mp_digit mu; + + /* can the fast reduction [comba] method be used? + * + * Note that unlike in mp_mul you're safely allowed *less* + * than the available columns [255 per default] since carries + * are fixed up in the inner loop. + */ + digs = n->used * 2 + 1; + if ((digs < MP_WARRAY) && + n->used < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_mp_montgomery_reduce (x, n, rho); + } + + /* grow the input as required */ + if (x->alloc < digs) { + if ((res = mp_grow (x, digs)) != MP_OKAY) { + return res; + } + } + x->used = digs; + + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * rho mod b + * + * The value of rho must be precalculated via + * bn_mp_montgomery_setup() such that + * it equals -1/n0 mod b this allows the + * following inner loop to reduce the + * input one digit at a time + */ + mu = (mp_digit) (((mp_word)x->dp[ix]) * ((mp_word)rho) & MP_MASK); + + /* a = a + mu * m * b**i */ + { + register int iy; + register mp_digit *tmpn, *tmpx, u; + register mp_word r; + + /* alias for digits of the modulus */ + tmpn = n->dp; + + /* alias for the digits of x [the input] */ + tmpx = x->dp + ix; + + /* set the carry to zero */ + u = 0; + + /* Multiply and add in place */ + for (iy = 0; iy < n->used; iy++) { + /* compute product and sum */ + r = ((mp_word)mu) * ((mp_word)*tmpn++) + + ((mp_word) u) + ((mp_word) * tmpx); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* fix digit */ + *tmpx++ = (mp_digit)(r & ((mp_word) MP_MASK)); + } + /* At this point the ix'th digit of x should be zero */ + + + /* propagate carries upwards as required*/ + while (u) { + *tmpx += u; + u = *tmpx >> DIGIT_BIT; + *tmpx++ &= MP_MASK; + } + } + } + + /* at this point the n.used'th least + * significant digits of x are all zero + * which means we can shift x to the + * right by n.used digits and the + * residue is unchanged. + */ + + /* x = x/b**n.used */ + mp_clamp(x); + mp_rshd (x, n->used); + + /* if x >= n then x = x - n */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + + return MP_OKAY; +} diff --git a/bn_mp_montgomery_setup.c b/bn_mp_montgomery_setup.c new file mode 100644 index 0000000..36253af --- /dev/null +++ b/bn_mp_montgomery_setup.c @@ -0,0 +1,53 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (((mp_digit) 1 << ((mp_digit) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} diff --git a/bn_mp_mul.c b/bn_mp_mul.c new file mode 100644 index 0000000..6f3c491 --- /dev/null +++ b/bn_mp_mul.c @@ -0,0 +1,48 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* use Toom-Cook? */ + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + /* use Karatsuba? */ + } else if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + int digs = a->used + b->used + 1; + + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } 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..f947d75 --- /dev/null +++ b/bn_mp_mul_2.c @@ -0,0 +1,76 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = a*2 */ +int mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accomodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} diff --git a/bn_mp_mul_2d.c b/bn_mp_mul_2d.c new file mode 100644 index 0000000..f5fa1d0 --- /dev/null +++ b/bn_mp_mul_2d.c @@ -0,0 +1,79 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* shift left by a certain bit count */ +int mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + 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 (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + 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..658fe01 --- /dev/null +++ b/bn_mp_mul_d.c @@ -0,0 +1,72 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* multiply by a digit */ +int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] */ + *tmpc++ = u; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} diff --git a/bn_mp_mulmod.c b/bn_mp_mulmod.c new file mode 100644 index 0000000..77f8459 --- /dev/null +++ b/bn_mp_mulmod.c @@ -0,0 +1,35 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..a79af17 --- /dev/null +++ b/bn_mp_n_root.c @@ -0,0 +1,126 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* find the n'th root of an integer + * + * Result found such that (c)**b <= a and (c+1)**b > a + * + * This algorithm uses Newton's approximation + * x[i+1] = x[i] - f(x[i])/f'(x[i]) + * which will find the root in log(N) time where + * each step involves a fair bit. This is not meant to + * find huge roots [square and cube, etc]. + */ +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))) */ + + /* t3 = t1**(b-1) */ + if ((res = mp_expt_d (&t1, b - 1, &t3)) != MP_OKAY) { + goto __T3; + } + + /* numerator */ + /* t2 = t1**b */ + if ((res = mp_mul (&t3, &t1, &t2)) != MP_OKAY) { + goto __T3; + } + + /* t2 = t1**b - a */ + if ((res = mp_sub (&t2, a, &t2)) != MP_OKAY) { + goto __T3; + } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if ((res = mp_mul_d (&t3, b, &t3)) != MP_OKAY) { + goto __T3; + } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if ((res = mp_div (&t2, &t3, &t3, NULL)) != MP_OKAY) { + 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..f9de6e4 --- /dev/null +++ b/bn_mp_neg.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = -a */ +int mp_neg (mp_int * a, mp_int * b) +{ + int res; + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + if (mp_iszero(b) != MP_YES) { + 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..793e8e9 --- /dev/null +++ b/bn_mp_or.c @@ -0,0 +1,44 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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_prime_fermat.c b/bn_mp_prime_fermat.c new file mode 100644 index 0000000..7d58785 --- /dev/null +++ b/bn_mp_prime_fermat.c @@ -0,0 +1,56 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* performs one Fermat test. + * + * If "a" were prime then b**a == b (mod a) since the order of + * the multiplicative sub-group would be phi(a) = a-1. That means + * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). + * + * Sets result to 1 if the congruence holds, or zero otherwise. + */ +int mp_prime_fermat (mp_int * a, mp_int * b, int *result) +{ + mp_int t; + int err; + + /* default to composite */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* init t */ + if ((err = mp_init (&t)) != MP_OKAY) { + return err; + } + + /* compute t = b**a mod a */ + if ((err = mp_exptmod (b, a, a, &t)) != MP_OKAY) { + goto __T; + } + + /* is it equal to b? */ + if (mp_cmp (&t, b) == MP_EQ) { + *result = MP_YES; + } + + err = MP_OKAY; +__T:mp_clear (&t); + return err; +} diff --git a/bn_mp_prime_is_divisible.c b/bn_mp_prime_is_divisible.c new file mode 100644 index 0000000..86392d1 --- /dev/null +++ b/bn_mp_prime_is_divisible.c @@ -0,0 +1,44 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines if an integers is divisible by one + * of the first PRIME_SIZE primes or not + * + * sets result to 0 if not, 1 if yes + */ +int mp_prime_is_divisible (mp_int * a, int *result) +{ + int err, ix; + mp_digit res; + + /* default to not */ + *result = MP_NO; + + for (ix = 0; ix < PRIME_SIZE; ix++) { + /* what is a mod __prime_tab[ix] */ + if ((err = mp_mod_d (a, __prime_tab[ix], &res)) != MP_OKAY) { + return err; + } + + /* is the residue zero? */ + if (res == 0) { + *result = MP_YES; + return MP_OKAY; + } + } + + return MP_OKAY; +} diff --git a/bn_mp_prime_is_prime.c b/bn_mp_prime_is_prime.c new file mode 100644 index 0000000..88a777b --- /dev/null +++ b/bn_mp_prime_is_prime.c @@ -0,0 +1,77 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* performs a variable number of rounds of Miller-Rabin + * + * Probability of error after t rounds is no more than + * (1/4)^t when 1 <= t <= PRIME_SIZE + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime (mp_int * a, int t, int *result) +{ + mp_int b; + int ix, err, res; + + /* default to no */ + *result = MP_NO; + + /* valid value of t? */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* is the input equal to one of the primes in the table? */ + for (ix = 0; ix < PRIME_SIZE; ix++) { + if (mp_cmp_d(a, __prime_tab[ix]) == MP_EQ) { + *result = 1; + return MP_OKAY; + } + } + + /* first perform trial division */ + if ((err = mp_prime_is_divisible (a, &res)) != MP_OKAY) { + return err; + } + + /* return if it was trivially divisible */ + if (res == MP_YES) { + return MP_OKAY; + } + + /* now perform the miller-rabin rounds */ + if ((err = mp_init (&b)) != MP_OKAY) { + return err; + } + + for (ix = 0; ix < t; ix++) { + /* set the prime */ + mp_set (&b, __prime_tab[ix]); + + if ((err = mp_prime_miller_rabin (a, &b, &res)) != MP_OKAY) { + goto __B; + } + + if (res == MP_NO) { + goto __B; + } + } + + /* passed the test */ + *result = MP_YES; +__B:mp_clear (&b); + return err; +} diff --git a/bn_mp_prime_miller_rabin.c b/bn_mp_prime_miller_rabin.c new file mode 100644 index 0000000..0c55d9c --- /dev/null +++ b/bn_mp_prime_miller_rabin.c @@ -0,0 +1,97 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Miller-Rabin test of "a" to the base of "b" as described in + * HAC pp. 139 Algorithm 4.24 + * + * Sets result to 0 if definitely composite or 1 if probably prime. + * Randomly the chance of error is no more than 1/4 and often + * very much lower. + */ +int mp_prime_miller_rabin (mp_int * a, mp_int * b, int *result) +{ + mp_int n1, y, r; + int s, j, err; + + /* default */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* get n1 = a - 1 */ + if ((err = mp_init_copy (&n1, a)) != MP_OKAY) { + return err; + } + if ((err = mp_sub_d (&n1, 1, &n1)) != MP_OKAY) { + goto __N1; + } + + /* set 2**s * r = n1 */ + if ((err = mp_init_copy (&r, &n1)) != MP_OKAY) { + goto __N1; + } + + /* count the number of least significant bits + * which are zero + */ + s = mp_cnt_lsb(&r); + + /* now divide n - 1 by 2**s */ + if ((err = mp_div_2d (&r, s, &r, NULL)) != MP_OKAY) { + goto __R; + } + + /* compute y = b**r mod a */ + if ((err = mp_init (&y)) != MP_OKAY) { + goto __R; + } + if ((err = mp_exptmod (b, &r, a, &y)) != MP_OKAY) { + goto __Y; + } + + /* if y != 1 and y != n1 do */ + if (mp_cmp_d (&y, 1) != MP_EQ && mp_cmp (&y, &n1) != MP_EQ) { + j = 1; + /* while j <= s-1 and y != n1 */ + while ((j <= (s - 1)) && mp_cmp (&y, &n1) != MP_EQ) { + if ((err = mp_sqrmod (&y, a, &y)) != MP_OKAY) { + goto __Y; + } + + /* if y == 1 then composite */ + if (mp_cmp_d (&y, 1) == MP_EQ) { + goto __Y; + } + + ++j; + } + + /* if y != n1 then composite */ + if (mp_cmp (&y, &n1) != MP_EQ) { + goto __Y; + } + } + + /* probably prime now */ + *result = MP_YES; +__Y:mp_clear (&y); +__R:mp_clear (&r); +__N1:mp_clear (&n1); + return err; +} diff --git a/bn_mp_prime_next_prime.c b/bn_mp_prime_next_prime.c new file mode 100644 index 0000000..f45af81 --- /dev/null +++ b/bn_mp_prime_next_prime.c @@ -0,0 +1,164 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style) +{ + int err, res, x, y; + mp_digit res_tab[PRIME_SIZE], step, kstep; + mp_int b; + + /* ensure t is valid */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* force positive */ + a->sign = MP_ZPOS; + + /* simple algo if a is less than the largest prime in the table */ + if (mp_cmp_d(a, __prime_tab[PRIME_SIZE-1]) == MP_LT) { + /* find which prime it is bigger than */ + for (x = PRIME_SIZE - 2; x >= 0; x--) { + if (mp_cmp_d(a, __prime_tab[x]) != MP_LT) { + if (bbs_style == 1) { + /* ok we found a prime smaller or + * equal [so the next is larger] + * + * however, the prime must be + * congruent to 3 mod 4 + */ + if ((__prime_tab[x + 1] & 3) != 3) { + /* scan upwards for a prime congruent to 3 mod 4 */ + for (y = x + 1; y < PRIME_SIZE; y++) { + if ((__prime_tab[y] & 3) == 3) { + mp_set(a, __prime_tab[y]); + return MP_OKAY; + } + } + } + } else { + mp_set(a, __prime_tab[x + 1]); + return MP_OKAY; + } + } + } + /* at this point a maybe 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + mp_set(a, 2); + return MP_OKAY; + } + /* fall through to the sieve */ + } + + /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ + if (bbs_style == 1) { + kstep = 4; + } else { + kstep = 2; + } + + /* at this point we will use a combination of a sieve and Miller-Rabin */ + + if (bbs_style == 1) { + /* if a mod 4 != 3 subtract the correct value to make it so */ + if ((a->dp[0] & 3) != 3) { + if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { return err; }; + } + } else { + if (mp_iseven(a) == 1) { + /* force odd */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + return err; + } + } + } + + /* generate the restable */ + for (x = 1; x < PRIME_SIZE; x++) { + if ((err = mp_mod_d(a, __prime_tab[x], res_tab + x)) != MP_OKAY) { + return err; + } + } + + /* init temp used for Miller-Rabin Testing */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for (;;) { + /* skip to the next non-trivially divisible candidate */ + step = 0; + do { + /* y == 1 if any residue was zero [e.g. cannot be prime] */ + y = 0; + + /* increase step to next candidate */ + step += kstep; + + /* compute the new residue without using division */ + for (x = 1; x < PRIME_SIZE; x++) { + /* add the step to each residue */ + res_tab[x] += kstep; + + /* subtract the modulus [instead of using division] */ + if (res_tab[x] >= __prime_tab[x]) { + res_tab[x] -= __prime_tab[x]; + } + + /* set flag if zero */ + if (res_tab[x] == 0) { + y = 1; + } + } + } while (y == 1 && step < ((((mp_digit)1)<= ((((mp_digit)1)< + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ + +/* This is possibly the mother of all prime generation functions, muahahahahaha! */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) +{ + unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; + int res, err, bsize, maskOR_msb_offset; + + /* sanity check the input */ + if (size <= 1 || t <= 0) { + return MP_VAL; + } + + /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ + if (flags & LTM_PRIME_SAFE) { + flags |= LTM_PRIME_BBS; + } + + /* calc the byte size */ + bsize = (size>>3)+(size&7?1:0); + + /* we need a buffer of bsize bytes */ + tmp = OPT_CAST(unsigned char) XMALLOC(bsize); + if (tmp == NULL) { + return MP_MEM; + } + + /* calc the maskAND value for the MSbyte*/ + maskAND = 0xFF >> (8 - (size & 7)); + + /* calc the maskOR_msb */ + maskOR_msb = 0; + maskOR_msb_offset = (size - 2) >> 3; + if (flags & LTM_PRIME_2MSB_ON) { + maskOR_msb |= 1 << ((size - 2) & 7); + } else if (flags & LTM_PRIME_2MSB_OFF) { + maskAND &= ~(1 << ((size - 2) & 7)); + } + + /* get the maskOR_lsb */ + maskOR_lsb = 0; + if (flags & LTM_PRIME_BBS) { + maskOR_lsb |= 3; + } + + do { + /* read the bytes */ + if (cb(tmp, bsize, dat) != bsize) { + err = MP_VAL; + goto error; + } + + /* work over the MSbyte */ + tmp[0] &= maskAND; + tmp[0] |= 1 << ((size - 1) & 7); + + /* mix in the maskORs */ + tmp[maskOR_msb_offset] |= maskOR_msb; + tmp[bsize-1] |= maskOR_lsb; + + /* read it in */ + if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + + if (flags & LTM_PRIME_SAFE) { + /* see if (a-1)/2 is prime */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { goto error; } + if ((err = mp_div_2(a, a)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + } + } while (res == MP_NO); + + if (flags & LTM_PRIME_SAFE) { + /* restore a to the original value */ + if ((err = mp_mul_2(a, a)) != MP_OKAY) { goto error; } + if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { goto error; } + } + + err = MP_OKAY; +error: + XFREE(tmp); + return err; +} + + diff --git a/bn_mp_radix_size.c b/bn_mp_radix_size.c new file mode 100644 index 0000000..ae2d09a --- /dev/null +++ b/bn_mp_radix_size.c @@ -0,0 +1,65 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* returns size of ASCII reprensentation */ +int mp_radix_size (mp_int * a, int radix, int *size) +{ + int res, digs; + mp_int t; + mp_digit d; + + *size = 0; + + /* special case for binary */ + if (radix == 2) { + *size = mp_count_bits (a) + (a->sign == MP_NEG ? 1 : 0) + 1; + return MP_OKAY; + } + + /* make sure the radix is in range */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* init a copy of the input */ + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* digs is the digit count */ + digs = 0; + + /* if it's negative add one for the sign */ + if (t.sign == MP_NEG) { + ++digs; + t.sign = MP_ZPOS; + } + + /* fetch out all of the digits */ + while (mp_iszero (&t) == 0) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + ++digs; + } + mp_clear (&t); + + /* return digs + 1, the 1 is for the NULL byte that would be required. */ + *size = digs + 1; + return MP_OKAY; +} + diff --git a/bn_mp_radix_smap.c b/bn_mp_radix_smap.c new file mode 100644 index 0000000..2c4d966 --- /dev/null +++ b/bn_mp_radix_smap.c @@ -0,0 +1,18 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* chars used in radix conversions */ +const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; diff --git a/bn_mp_rand.c b/bn_mp_rand.c new file mode 100644 index 0000000..c744994 --- /dev/null +++ b/bn_mp_rand.c @@ -0,0 +1,49 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 */ + do { + d = ((mp_digit) abs (rand ())); + } while (d == 0); + + if ((res = mp_add_d (a, d, a)) != MP_OKAY) { + return res; + } + + while (digits-- > 0) { + if ((res = mp_lshd (a, 1)) != MP_OKAY) { + return res; + } + + if ((res = mp_add_d (a, ((mp_digit) abs (rand ())), a)) != MP_OKAY) { + return res; + } + } + + return MP_OKAY; +} diff --git a/bn_mp_read_radix.c b/bn_mp_read_radix.c new file mode 100644 index 0000000..653a891 --- /dev/null +++ b/bn_mp_read_radix.c @@ -0,0 +1,76 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* 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; + + /* make sure the radix is ok */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* if the leading digit is a + * minus set the sign to negative. + */ + if (*str == '-') { + ++str; + neg = MP_NEG; + } else { + neg = MP_ZPOS; + } + + /* set the integer to the default of zero */ + mp_zero (a); + + /* process each digit of the string */ + while (*str) { + /* if the radix < 36 the conversion is case insensitive + * this allows numbers like 1AB and 1ab to represent the same value + * [e.g. in hex] + */ + ch = (char) ((radix < 36) ? toupper (*str) : *str); + for (y = 0; y < 64; y++) { + if (ch == mp_s_rmap[y]) { + break; + } + } + + /* if the char was found in the map + * and is less than the given radix add it + * to the number, otherwise exit the loop. + */ + 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; + } + + /* set the sign only if a != 0 */ + if (mp_iszero(a) != 1) { + a->sign = neg; + } + 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..39d8618 --- /dev/null +++ b/bn_mp_read_signed_bin.c @@ -0,0 +1,36 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* read magnitude */ + if ((res = mp_read_unsigned_bin (a, b + 1, c - 1)) != MP_OKAY) { + return res; + } + + /* first byte is 0 for positive, non-zero for negative */ + if (b[0] == 0) { + a->sign = MP_ZPOS; + } else { + a->sign = 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..cb11d87 --- /dev/null +++ b/bn_mp_read_unsigned_bin.c @@ -0,0 +1,50 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} diff --git a/bn_mp_reduce.c b/bn_mp_reduce.c new file mode 100644 index 0000000..d4c5b10 --- /dev/null +++ b/bn_mp_reduce.c @@ -0,0 +1,84 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* 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; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + 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; + } + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 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) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} diff --git a/bn_mp_reduce_2k.c b/bn_mp_reduce_2k.c new file mode 100644 index 0000000..f6b40cf --- /dev/null +++ b/bn_mp_reduce_2k.c @@ -0,0 +1,56 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* reduces a modulo n where n is of the form 2**p - d */ +int +mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (d != 1) { + /* q = q * d */ + if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + diff --git a/bn_mp_reduce_2k_setup.c b/bn_mp_reduce_2k_setup.c new file mode 100644 index 0000000..6bdfe7f --- /dev/null +++ b/bn_mp_reduce_2k_setup.c @@ -0,0 +1,42 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines the setup value */ +int +mp_reduce_2k_setup(mp_int *a, mp_digit *d) +{ + int res, p; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(a); + if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + *d = tmp.dp[0]; + mp_clear(&tmp); + return MP_OKAY; +} diff --git a/bn_mp_reduce_is_2k.c b/bn_mp_reduce_is_2k.c new file mode 100644 index 0000000..d43b9ff --- /dev/null +++ b/bn_mp_reduce_is_2k.c @@ -0,0 +1,45 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines if mp_reduce_2k can be used */ +int mp_reduce_is_2k(mp_int *a) +{ + int ix, iy, iz, iw; + + if (a->used == 0) { + return 0; + } else if (a->used == 1) { + return 1; + } else if (a->used > 1) { + iy = mp_count_bits(a); + iz = 1; + iw = 1; + + /* Test every bit from the second digit up, must be 1 */ + for (ix = DIGIT_BIT; ix < iy; ix++) { + if ((a->dp[iw] & iz) == 0) { + return 0; + } + iz <<= 1; + if (iz > (int)MP_MASK) { + ++iw; + iz = 1; + } + } + } + return 1; +} + diff --git a/bn_mp_reduce_setup.c b/bn_mp_reduce_setup.c new file mode 100644 index 0000000..1ba69e9 --- /dev/null +++ b/bn_mp_reduce_setup.c @@ -0,0 +1,29 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + } + return mp_div (a, b, a, NULL); +} diff --git a/bn_mp_rshd.c b/bn_mp_rshd.c new file mode 100644 index 0000000..87d6e3b --- /dev/null +++ b/bn_mp_rshd.c @@ -0,0 +1,66 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} diff --git a/bn_mp_set.c b/bn_mp_set.c new file mode 100644 index 0000000..25ccf64 --- /dev/null +++ b/bn_mp_set.c @@ -0,0 +1,23 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..64f1aed --- /dev/null +++ b/bn_mp_set_int.c @@ -0,0 +1,42 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 */ + 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 += 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..daefed2 --- /dev/null +++ b/bn_mp_shrink.c @@ -0,0 +1,29 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* shrink a bignum */ +int mp_shrink (mp_int * a) +{ + mp_digit *tmp; + if (a->alloc != a->used && a->used > 0) { + if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * a->used)) == NULL) { + return MP_MEM; + } + a->dp = tmp; + 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..76d085c --- /dev/null +++ b/bn_mp_signed_bin_size.c @@ -0,0 +1,21 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..4857994 --- /dev/null +++ b/bn_mp_sqr.c @@ -0,0 +1,41 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes b = a*a */ +int +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else { + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_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..778b68e --- /dev/null +++ b/bn_mp_sqrmod.c @@ -0,0 +1,35 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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_sqrt.c b/bn_mp_sqrt.c new file mode 100644 index 0000000..ec9d102 --- /dev/null +++ b/bn_mp_sqrt.c @@ -0,0 +1,75 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* this function is less generic than mp_n_root, simpler and faster */ +int mp_sqrt(mp_int *arg, mp_int *ret) +{ + int res; + mp_int t1,t2; + + /* must be positive */ + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* easy out */ + if (mp_iszero(arg) == MP_YES) { + mp_zero(ret); + return MP_OKAY; + } + + if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto E2; + } + + /* First approx. (not very bad for large arg) */ + mp_rshd (&t1,t1.used/2); + + /* t1 > 0 */ + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* And now t1 > sqrt(arg) */ + do { + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* t1 >= sqrt(arg) >= t2 at this point */ + } while (mp_cmp_mag(&t1,&t2) == MP_GT); + + mp_exch(&t1,ret); + +E1: mp_clear(&t2); +E2: mp_clear(&t1); + return res; +} + diff --git a/bn_mp_sub.c b/bn_mp_sub.c new file mode 100644 index 0000000..eb99583 --- /dev/null +++ b/bn_mp_sub.c @@ -0,0 +1,53 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + diff --git a/bn_mp_sub_d.c b/bn_mp_sub_d.c new file mode 100644 index 0000000..6368970 --- /dev/null +++ b/bn_mp_sub_d.c @@ -0,0 +1,83 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* single digit subtraction */ +int +mp_sub_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit *tmpa, *tmpc, mu; + int res, ix, oldused; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative just do an unsigned + * addition [with fudged signs] + */ + if (a->sign == MP_NEG) { + a->sign = MP_ZPOS; + res = mp_add_d(a, b, c); + a->sign = c->sign = MP_NEG; + return res; + } + + /* setup regs */ + oldused = c->used; + tmpa = a->dp; + tmpc = c->dp; + + /* if a <= b simply fix the single digit */ + if ((a->used == 1 && a->dp[0] <= b) || a->used == 0) { + if (a->used == 1) { + *tmpc++ = b - *tmpa; + } else { + *tmpc++ = b; + } + ix = 1; + + /* negative/1digit */ + c->sign = MP_NEG; + c->used = 1; + } else { + /* positive/size */ + c->sign = MP_ZPOS; + c->used = a->used; + + /* subtract first digit */ + *tmpc = *tmpa++ - b; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + + /* handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ - mu; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + } + } + + /* zero excess digits */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + return MP_OKAY; +} + diff --git a/bn_mp_submod.c b/bn_mp_submod.c new file mode 100644 index 0000000..868dd2f --- /dev/null +++ b/bn_mp_submod.c @@ -0,0 +1,36 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..1e21667 --- /dev/null +++ b/bn_mp_to_signed_bin.c @@ -0,0 +1,28 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..f7cae69 --- /dev/null +++ b/bn_mp_to_unsigned_bin.c @@ -0,0 +1,43 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + 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_toom_mul.c b/bn_mp_toom_mul.c new file mode 100644 index 0000000..50660ff --- /dev/null +++ b/bn_mp_toom_mul.c @@ -0,0 +1,272 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* multiplication using the Toom-Cook 3-way algorithm */ +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = MIN(a->used, b->used) / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* b = b2 * B**2 + b1 * B + b0 */ + if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(b, &b1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b1, B); + mp_mod_2d(&b1, DIGIT_BIT * B, &b1); + + if ((res = mp_copy(b, &b2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b2, B*2); + + /* w0 = a0*b0 */ + if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * b2 */ + if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, + 2 small divisions and 1 small multiplication + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL); + return res; +} + diff --git a/bn_mp_toom_sqr.c b/bn_mp_toom_sqr.c new file mode 100644 index 0000000..de3094a --- /dev/null +++ b/bn_mp_toom_sqr.c @@ -0,0 +1,220 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* squaring using Toom-Cook 3-way algorithm */ +int +mp_toom_sqr(mp_int *a, mp_int *b) +{ + mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = a->used / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* w0 = a0*a0 */ + if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * a2 */ + if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))**2 */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))**2 */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)**2 */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); + return res; +} + diff --git a/bn_mp_toradix.c b/bn_mp_toradix.c new file mode 100644 index 0000000..c605867 --- /dev/null +++ b/bn_mp_toradix.c @@ -0,0 +1,69 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* 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; + + /* check range of the radix */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == 1) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + 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++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear (&t); + return MP_OKAY; +} + diff --git a/bn_mp_toradix_n.c b/bn_mp_toradix_n.c new file mode 100644 index 0000000..d2f6ec2 --- /dev/null +++ b/bn_mp_toradix_n.c @@ -0,0 +1,83 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* stores a bignum as a ASCII string in a given radix (2..64) + * + * Stores upto maxlen-1 chars and always a NULL byte + */ +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen) +{ + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the maxlen, radix */ + if (maxlen < 3 || radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == 1) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + /* we have to reverse our digits later... but not the - sign!! */ + ++_s; + + /* store the flag and mark the number as positive */ + *str++ = '-'; + t.sign = MP_ZPOS; + + /* subtract a char */ + --maxlen; + } + + 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++ = mp_s_rmap[d]; + ++digs; + + if (--maxlen == 1) { + /* no more room */ + break; + } + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + 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..47bbc64 --- /dev/null +++ b/bn_mp_unsigned_bin_size.c @@ -0,0 +1,23 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..36e6733 --- /dev/null +++ b/bn_mp_xor.c @@ -0,0 +1,45 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..a888589 --- /dev/null +++ b/bn_mp_zero.c @@ -0,0 +1,24 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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_prime_sizes_tab.c b/bn_prime_sizes_tab.c new file mode 100644 index 0000000..c6e1dad --- /dev/null +++ b/bn_prime_sizes_tab.c @@ -0,0 +1,51 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* this table gives the # of rabin miller trials for a prob of failure lower than 2^-96 */ +static const struct { + int k, t; +} sizes[] = { +{ 128, 28 }, +{ 256, 16 }, +{ 384, 10 }, +{ 512, 7 }, +{ 640, 6 }, +{ 768, 5 }, +{ 896, 4 }, +{ 1024, 4 }, +{ 1152, 3 }, +{ 1280, 3 }, +{ 1408, 3 }, +{ 1536, 3 }, +{ 1664, 3 }, +{ 1792, 2 } }; + +/* returns # of RM trials required for a given bit size */ +int mp_prime_rabin_miller_trials(int size) +{ + int x; + + for (x = 0; x < (int)(sizeof(sizes)/(sizeof(sizes[0]))); x++) { + if (sizes[x].k == size) { + return sizes[x].t; + } else if (sizes[x].k > size) { + return (x == 0) ? sizes[0].t : sizes[x - 1].t; + } + } + return 1; +} + + diff --git a/bn_prime_tab.c b/bn_prime_tab.c new file mode 100644 index 0000000..7dfdc8c --- /dev/null +++ b/bn_prime_tab.c @@ -0,0 +1,55 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include +const mp_digit __prime_tab[] = { + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, +#ifndef MP_8BIT + 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 +#endif +}; diff --git a/bn_reverse.c b/bn_reverse.c new file mode 100644 index 0000000..89cbbb7 --- /dev/null +++ b/bn_reverse.c @@ -0,0 +1,33 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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..e07931a --- /dev/null +++ b/bn_s_mp_add.c @@ -0,0 +1,103 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* 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 { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= 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 */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} diff --git a/bn_s_mp_exptmod.c b/bn_s_mp_exptmod.c new file mode 100644 index 0000000..28c14fd --- /dev/null +++ b/bn_s_mp_exptmod.c @@ -0,0 +1,234 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + 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; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + 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 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, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + 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 = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)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 */ + /* 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 __RES; + } + if ((err = mp_reduce (&res, P, &mu)) != MP_OKAY) { + goto __RES; + } + + /* empty window and reset */ + bitcpy = 0; + 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: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} diff --git a/bn_s_mp_mul_digs.c b/bn_s_mp_mul_digs.c new file mode 100644 index 0000000..a5b1067 --- /dev/null +++ b/bn_s_mp_mul_digs.c @@ -0,0 +1,85 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + 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 */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + 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)); + } + /* set carry if it is placed below digs */ + 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..84cc7d0 --- /dev/null +++ b/bn_s_mp_mul_high_digs.c @@ -0,0 +1,73 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } + + 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..3a00a4e --- /dev/null +++ b/bn_s_mp_sqr.c @@ -0,0 +1,79 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*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 = (mp_digit)(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 + (2*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 it's 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 = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(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..4381af8 --- /dev/null +++ b/bn_s_mp_sub.c @@ -0,0 +1,83 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* 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; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + diff --git a/bncore.c b/bncore.c new file mode 100644 index 0000000..acb78a0 --- /dev/null +++ b/bncore.c @@ -0,0 +1,31 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Known optimal configurations + + CPU /Compiler /MUL CUTOFF/SQR CUTOFF +------------------------------------------------------------- + Intel P4 /GCC v3.2 / 70/ 108 + AMD Athlon XP /GCC v3.2 / 109/ 127 + +*/ + +/* configured for a AMD XP Thoroughbred core with etc/tune.c */ +int KARATSUBA_MUL_CUTOFF = 109, /* Min. number of digits before Karatsuba multiplication is used. */ + KARATSUBA_SQR_CUTOFF = 127, /* Min. number of digits before Karatsuba squaring is used. */ + + TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ + TOOM_SQR_CUTOFF = 400; diff --git a/booker.pl b/booker.pl new file mode 100644 index 0000000..4f4231f --- /dev/null +++ b/booker.pl @@ -0,0 +1,261 @@ +#!/bin/perl +# +#Used to prepare the book "tommath.src" for LaTeX by pre-processing it into a .tex file +# +#Essentially you write the "tommath.src" as normal LaTex except where you want code snippets you put +# +#EXAM,file +# +#This preprocessor will then open "file" and insert it as a verbatim copy. +# +#Tom St Denis + +#get graphics type +if (shift =~ /PDF/) { + $graph = ""; +} else { + $graph = ".ps"; +} + +open(IN,"tommath.tex") or die "Can't open destination file"; + +print "Scanning for sections\n"; +$chapter = $section = $subsection = 0; +$x = 0; +while () { + print "."; + if (!(++$x % 80)) { print "\n"; } + #update the headings + if (~($_ =~ /\*/)) { + if ($_ =~ /\\chapter{.+}/) { + ++$chapter; + $section = $subsection = 0; + } elsif ($_ =~ /\\section{.+}/) { + ++$section; + $subsection = 0; + } elsif ($_ =~ /\\subsection{.+}/) { + ++$subsection; + } + } + + if ($_ =~ m/MARK/) { + @m = split(",",$_); + chomp(@m[1]); + $index1{@m[1]} = $chapter; + $index2{@m[1]} = $section; + $index3{@m[1]} = $subsection; + } +} +close(IN); + +open(IN,") { + ++$readline; + ++$srcline; + + if ($_ =~ m/MARK/) { + } elsif ($_ =~ m/EXAM/ || $_ =~ m/LIST/) { + if ($_ =~ m/EXAM/) { + $skipheader = 1; + } else { + $skipheader = 0; + } + + # EXAM,file + chomp($_); + @m = split(",",$_); + open(SRC,"<$m[1]") or die "Error:$srcline:Can't open source file $m[1]"; + + print "$srcline:Inserting $m[1]:"; + + $line = 0; + $tmp = $m[1]; + $tmp =~ s/_/"\\_"/ge; + print OUT "\\vspace{+3mm}\\begin{small}\n\\hspace{-5.1mm}{\\bf File}: $tmp\n\\vspace{-3mm}\n\\begin{alltt}\n"; + $wroteline += 5; + + if ($skipheader == 1) { + # scan till next end of comment, e.g. skip license + while () { + $text[$line++] = $_; + last if ($_ =~ /tommath\.h/); + } + } + + $inline = 0; + while () { + $text[$line++] = $_; + ++$inline; + chomp($_); + $_ =~ s/\t/" "/ge; + $_ =~ s/{/"^{"/ge; + $_ =~ s/}/"^}"/ge; + $_ =~ s/\\/'\symbol{92}'/ge; + $_ =~ s/\^/"\\"/ge; + + printf OUT ("%03d ", $line); + for ($x = 0; $x < length($_); $x++) { + print OUT chr(vec($_, $x, 8)); + if ($x == 75) { + print OUT "\n "; + ++$wroteline; + } + } + print OUT "\n"; + ++$wroteline; + } + $totlines = $line; + print OUT "\\end{alltt}\n\\end{small}\n"; + close(SRC); + print "$inline lines\n"; + $wroteline += 2; + } elsif ($_ =~ m/@\d+,.+@/) { + # line contains [number,text] + # e.g. @14,for (ix = 0)@ + $txt = $_; + while ($txt =~ m/@\d+,.+@/) { + @m = split("@",$txt); # splits into text, one, two + @parms = split(",",$m[1]); # splits one,two into two elements + + # now search from $parms[0] down for $parms[1] + $found1 = 0; + $found2 = 0; + for ($i = $parms[0]; $i < $totlines && $found1 == 0; $i++) { + if ($text[$i] =~ m/\Q$parms[1]\E/) { + $foundline1 = $i + 1; + $found1 = 1; + } + } + + # now search backwards + for ($i = $parms[0] - 1; $i >= 0 && $found2 == 0; $i--) { + if ($text[$i] =~ m/\Q$parms[1]\E/) { + $foundline2 = $i + 1; + $found2 = 1; + } + } + + # now use the closest match or the first if tied + if ($found1 == 1 && $found2 == 0) { + $found = 1; + $foundline = $foundline1; + } elsif ($found1 == 0 && $found2 == 1) { + $found = 1; + $foundline = $foundline2; + } elsif ($found1 == 1 && $found2 == 1) { + $found = 1; + if (($foundline1 - $parms[0]) <= ($parms[0] - $foundline2)) { + $foundline = $foundline1; + } else { + $foundline = $foundline2; + } + } else { + $found = 0; + } + + # if found replace + if ($found == 1) { + $delta = $parms[0] - $foundline; + print "Found replacement tag for \"$parms[1]\" on line $srcline which refers to line $foundline (delta $delta)\n"; + $_ =~ s/@\Q$m[1]\E@/$foundline/; + } else { + print "ERROR: The tag \"$parms[1]\" on line $srcline was not found in the most recently parsed source!\n"; + } + + # remake the rest of the line + $cnt = @m; + $txt = ""; + for ($i = 2; $i < $cnt; $i++) { + $txt = $txt . $m[$i] . "@"; + } + } + print OUT $_; + ++$wroteline; + } elsif ($_ =~ /~.+~/) { + # line contains a ~text~ pair used to refer to indexing :-) + $txt = $_; + while ($txt =~ /~.+~/) { + @m = split("~", $txt); + + # word is the second position + $word = @m[1]; + $a = $index1{$word}; + $b = $index2{$word}; + $c = $index3{$word}; + + # if chapter (a) is zero it wasn't found + if ($a == 0) { + print "ERROR: the tag \"$word\" on line $srcline was not found previously marked.\n"; + } else { + # format the tag as x, x.y or x.y.z depending on the values + $str = $a; + $str = $str . ".$b" if ($b != 0); + $str = $str . ".$c" if ($c != 0); + + if ($b == 0 && $c == 0) { + # its a chapter + if ($a <= 10) { + if ($a == 1) { + $str = "chapter one"; + } elsif ($a == 2) { + $str = "chapter two"; + } elsif ($a == 3) { + $str = "chapter three"; + } elsif ($a == 4) { + $str = "chapter four"; + } elsif ($a == 5) { + $str = "chapter five"; + } elsif ($a == 6) { + $str = "chapter six"; + } elsif ($a == 7) { + $str = "chapter seven"; + } elsif ($a == 8) { + $str = "chapter eight"; + } elsif ($a == 9) { + $str = "chapter nine"; + } elsif ($a == 2) { + $str = "chapter ten"; + } + } else { + $str = "chapter " . $str; + } + } else { + $str = "section " . $str if ($b != 0 && $c == 0); + $str = "sub-section " . $str if ($b != 0 && $c != 0); + } + + #substitute + $_ =~ s/~\Q$word\E~/$str/; + + print "Found replacement tag for marker \"$word\" on line $srcline which refers to $str\n"; + } + + # remake rest of the line + $cnt = @m; + $txt = ""; + for ($i = 2; $i < $cnt; $i++) { + $txt = $txt . $m[$i] . "~"; + } + } + print OUT $_; + ++$wroteline; + } elsif ($_ =~ m/FIGU/) { + # FIGU,file,caption + chomp($_); + @m = split(",", $_); + print OUT "\\begin{center}\n\\begin{figure}[here]\n\\includegraphics{pics/$m[1]$graph}\n"; + print OUT "\\caption{$m[2]}\n\\label{pic:$m[1]}\n\\end{figure}\n\\end{center}\n"; + $wroteline += 4; + } else { + print OUT $_; + ++$wroteline; + } +} +print "Read $readline lines, wrote $wroteline lines\n"; + +close (OUT); +close (IN); diff --git a/changes.txt b/changes.txt new file mode 100644 index 0000000..63e9d61 --- /dev/null +++ b/changes.txt @@ -0,0 +1,304 @@ +April 11th, 2004 +v0.30 -- Added "mp_toradix_n" which stores upto "n-1" least significant digits of an mp_int + -- Johan Lindh sent a patch so MSVC wouldn't whine about redefining malloc [in weird dll modes] + -- Henrik Goldman spotted a missing OPT_CAST in mp_fwrite() + -- Tuned tommath.h so that when MP_LOW_MEM is defined MP_PREC shall be reduced. + [I also allow MP_PREC to be externally defined now] + -- Sped up mp_cnt_lsb() by using a 4x4 table [e.g. 4x speedup] + -- Added mp_prime_random_ex() which is a more versatile prime generator accurate to + exact bit lengths (unlike the deprecated but still available mp_prime_random() which + is only accurate to byte lengths). See the new LTM_PRIME_* flags ;-) + -- Alex Polushin contributed an optimized mp_sqrt() as well as mp_get_int() and mp_is_square(). + I've cleaned them all up to be a little more consistent [along with one bug fix] for this release. + -- Added mp_init_set and mp_init_set_int to initialize and set small constants with one function + call. + -- Removed /etclib directory [um LibTomPoly deprecates this]. + -- Fixed mp_mod() so the sign of the result agrees with the sign of the modulus. + ++ N.B. My semester is almost up so expect updates to the textbook to be posted to the libtomcrypt.org + website. + +Jan 25th, 2004 +v0.29 ++ Note: "Henrik" from the v0.28 changelog refers to Henrik Goldman ;-) + -- Added fix to mp_shrink to prevent a realloc when used == 0 [e.g. realloc zero bytes???] + -- Made the mp_prime_rabin_miller_trials() function internal table smaller and also + set the minimum number of tests to two (sounds a bit safer). + -- Added a mp_exteuclid() which computes the extended euclidean algorithm. + -- Fixed a memory leak in s_mp_exptmod() [called when Barrett reduction is to be used] which would arise + if a multiplication or subsequent reduction failed [would not free the temp result]. + -- Made an API change to mp_radix_size(). It now returns an error code and stores the required size + through an "int star" passed to it. + +Dec 24th, 2003 +v0.28 -- Henrik Goldman suggested I add casts to the montomgery code [stores into mu...] so compilers wouldn't + spew [erroneous] diagnostics... fixed. + -- Henrik Goldman also spotted two typos. One in mp_radix_size() and another in mp_toradix(). + -- Added fix to mp_shrink() to avoid a memory leak. + -- Added mp_prime_random() which requires a callback to make truly random primes of a given nature + (idea from chat with Niels Ferguson at Crypto'03) + -- Picked up a second wind. I'm filled with Gooo. Mission Gooo! + -- Removed divisions from mp_reduce_is_2k() + -- Sped up mp_div_d() [general case] to use only one division per digit instead of two. + -- Added the heap macros from LTC to LTM. Now you can easily [by editing four lines of tommath.h] + change the name of the heap functions used in LTM [also compatible with LTC via MPI mode] + -- Added bn_prime_rabin_miller_trials() which gives the number of Rabin-Miller trials to achieve + a failure rate of less than 2^-96 + -- fixed bug in fast_mp_invmod(). The initial testing logic was wrong. An invalid input is not when + "a" and "b" are even it's when "b" is even [the algo is for odd moduli only]. + -- Started a new manual [finally]. It is incomplete and will be finished as time goes on. I had to stop + adding full demos around half way in chapter three so I could at least get a good portion of the + manual done. If you really need help using the library you can always email me! + -- My Textbook is now included as part of the package [all Public Domain] + +Sept 19th, 2003 +v0.27 -- Removed changes.txt~ which was made by accident since "kate" decided it was + a good time to re-enable backups... [kde is fun!] + -- In mp_grow() "a->dp" is not overwritten by realloc call [re: memory leak] + Now if mp_grow() fails the mp_int is still valid and can be cleared via + mp_clear() to reclaim the memory. + -- Henrik Goldman found a buffer overflow bug in mp_add_d(). Fixed. + -- Cleaned up mp_mul_d() to be much easier to read and follow. + +Aug 29th, 2003 +v0.26 -- Fixed typo that caused warning with GCC 3.2 + -- Martin Marcel noticed a bug in mp_neg() that allowed negative zeroes. + Also, Martin is the fellow who noted the bugs in mp_gcd() of 0.24/0.25. + -- Martin Marcel noticed an optimization [and slight bug] in mp_lcm(). + -- Added fix to mp_read_unsigned_bin to prevent a buffer overflow. + -- Beefed up the comments in the baseline multipliers [and montgomery] + -- Added "mont" demo to the makefile.msvc in etc/ + -- Optimized sign compares in mp_cmp from 4 to 2 cases. + +Aug 4th, 2003 +v0.25 -- Fix to mp_gcd again... oops (0,-a) == (-a, 0) == a + -- Fix to mp_clear which didn't reset the sign [Greg Rose] + -- Added mp_error_to_string() to convert return codes to strings. [Greg Rose] + -- Optimized fast_mp_invmod() to do the test for invalid inputs [both even] + first so temps don't have to be initialized if it's going to fail. + -- Optimized mp_gcd() by removing mp_div_2d calls for when one of the inputs + is odd. + -- Tons of new comments, some indentation fixups, etc. + -- mp_jacobi() returns MP_VAL if the modulus is less than or equal to zero. + -- fixed two typos in the header of each file :-) + -- LibTomMath is officially Public Domain [see LICENSE] + +July 15th, 2003 +v0.24 -- Optimized mp_add_d and mp_sub_d to not allocate temporary variables + -- Fixed mp_gcd() so the gcd of 0,0 is 0. Allows the gcd operation to be chained + e.g. (0,0,a) == a [instead of 1] + -- Should be one of the last release for a while. Working on LibTomMath book now. + -- optimized the pprime demo [/etc/pprime.c] to first make a huge table of single + digit primes then it reads them randomly instead of randomly choosing/testing single + digit primes. + +July 12th, 2003 +v0.23 -- Optimized mp_prime_next_prime() to not use mp_mod [via is_divisible()] in each + iteration. Instead now a smaller table is kept of the residues which can be updated + without division. + -- Fixed a bug in next_prime() where an input of zero would be treated as odd and + have two added to it [to move to the next odd]. + -- fixed a bug in prime_fermat() and prime_miller_rabin() which allowed the base + to be negative, zero or one. Normally the test is only valid if the base is + greater than one. + -- changed the next_prime() prototype to accept a new parameter "bbs_style" which + will find the next prime congruent to 3 mod 4. The default [bbs_style==0] will + make primes which are either congruent to 1 or 3 mod 4. + -- fixed mp_read_unsigned_bin() so that it doesn't include both code for + the case DIGIT_BIT < 8 and >= 8 + -- optimized div_d() to easy out on division by 1 [or if a == 0] and use + logical shifts if the divisor is a power of two. + -- the default DIGIT_BIT type was not int for non-default builds. Fixed. + +July 2nd, 2003 +v0.22 -- Fixed up mp_invmod so the result is properly in range now [was always congruent to the inverse...] + -- Fixed up s_mp_exptmod and mp_exptmod_fast so the lower half of the pre-computed table isn't allocated + which makes the algorithm use half as much ram. + -- Fixed the install script not to make the book :-) [which isn't included anyways] + -- added mp_cnt_lsb() which counts how many of the lsbs are zero + -- optimized mp_gcd() to use the new mp_cnt_lsb() to replace multiple divisions by two by a single division. + -- applied similar optimization to mp_prime_miller_rabin(). + -- Fixed a bug in both mp_invmod() and fast_mp_invmod() which tested for odd + via "mp_iseven() == 0" which is not valid [since zero is not even either]. + +June 19th, 2003 +v0.21 -- Fixed bug in mp_mul_d which would not handle sign correctly [would not always forward it] + -- Removed the #line lines from gen.pl [was in violation of ISO C] + +June 8th, 2003 +v0.20 -- Removed the book from the package. Added the TDCAL license document. + -- This release is officially pure-bred TDCAL again [last officially TDCAL based release was v0.16] + +June 6th, 2003 +v0.19 -- Fixed a bug in mp_montgomery_reduce() which was introduced when I tweaked mp_rshd() in the previous release. + Essentially the digits were not trimmed before the compare which cause a subtraction to occur all the time. + -- Fixed up etc/tune.c a bit to stop testing new cutoffs after 16 failures [to find more optimal points]. + Brute force ho! + + +May 29th, 2003 +v0.18 -- Fixed a bug in s_mp_sqr which would handle carries properly just not very elegantly. + (e.g. correct result, just bad looking code) + -- Fixed bug in mp_sqr which still had a 512 constant instead of MP_WARRAY + -- Added Toom-Cook multipliers [needs tuning!] + -- Added efficient divide by 3 algorithm mp_div_3 + -- Re-wrote mp_div_d to be faster than calling mp_div + -- Added in a donated BCC makefile and a single page LTM poster (ahalhabsi@sbcglobal.net) + -- Added mp_reduce_2k which reduces an input modulo n = 2**p - k for any single digit k + -- Made the exptmod system be aware of the 2k reduction algorithms. + -- Rewrote mp_dr_reduce to be smaller, simpler and easier to understand. + +May 17th, 2003 +v0.17 -- Benjamin Goldberg submitted optimized mp_add and mp_sub routines. A new gen.pl as well + as several smaller suggestions. Thanks! + -- removed call to mp_cmp in inner loop of mp_div and put mp_cmp_mag in its place :-) + -- Fixed bug in mp_exptmod that would cause it to fail for odd moduli when DIGIT_BIT != 28 + -- mp_exptmod now also returns errors if the modulus is negative and will handle negative exponents + -- mp_prime_is_prime will now return true if the input is one of the primes in the prime table + -- Damian M Gryski (dgryski@uwaterloo.ca) found a index out of bounds error in the + mp_fast_s_mp_mul_high_digs function which didn't come up before. (fixed) + -- Refactored the DR reduction code so there is only one function per file. + -- Fixed bug in the mp_mul() which would erroneously avoid the faster multiplier [comba] when it was + allowed. The bug would not cause the incorrect value to be produced just less efficient (fixed) + -- Fixed similar bug in the Montgomery reduction code. + -- Added tons of (mp_digit) casts so the 7/15/28/31 bit digit code will work flawlessly out of the box. + Also added limited support for 64-bit machines with a 60-bit digit. Both thanks to Tom Wu (tom@arcot.com) + -- Added new comments here and there, cleaned up some code [style stuff] + -- Fixed a lingering typo in mp_exptmod* that would set bitcnt to zero then one. Very silly stuff :-) + -- Fixed up mp_exptmod_fast so it would set "redux" to the comba Montgomery reduction if allowed. This + saves quite a few calls and if statements. + -- Added etc/mont.c a test of the Montgomery reduction [assuming all else works :-| ] + -- Fixed up etc/tune.c to use a wider test range [more appropriate] also added a x86 based addition which + uses RDTSC for high precision timing. + -- Updated demo/demo.c to remove MPI stuff [won't work anyways], made the tests run for 2 seconds each so its + not so insanely slow. Also made the output space delimited [and fixed up various errors] + -- Added logs directory, logs/graph.dem which will use gnuplot to make a series of PNG files + that go with the pre-made index.html. You have to build [via make timing] and run ltmtest first in the + root of the package. + -- Fixed a bug in mp_sub and mp_add where "-a - -a" or "-a + a" would produce -0 as the result [obviously invalid]. + -- Fixed a bug in mp_rshd. If the count == a.used it should zero/return [instead of shifting] + -- Fixed a "off-by-one" bug in mp_mul2d. The initial size check on alloc would be off by one if the residue + shifting caused a carry. + -- Fixed a bug where s_mp_mul_digs() would not call the Comba based routine if allowed. This made Barrett reduction + slower than it had to be. + +Mar 29th, 2003 +v0.16 -- Sped up mp_div by making normalization one shift call + -- Sped up mp_mul_2d/mp_div_2d by aliasing pointers :-) + -- Cleaned up mp_gcd to use the macros for odd/even detection + -- Added comments here and there, mostly there but occasionally here too. + +Mar 22nd, 2003 +v0.15 -- Added series of prime testing routines to lib + -- Fixed up etc/tune.c + -- Added DR reduction algorithm + -- Beefed up the manual more. + -- Fixed up demo/demo.c so it doesn't have so many warnings and it does the full series of + tests + -- Added "pre-gen" directory which will hold a "gen.pl"'ed copy of the entire lib [done at + zipup time so its always the latest] + -- Added conditional casts for C++ users [boo!] + +Mar 15th, 2003 +v0.14 -- Tons of manual updates + -- cleaned up the directory + -- added MSVC makefiles + -- source changes [that I don't recall] + -- Fixed up the lshd/rshd code to use pointer aliasing + -- Fixed up the mul_2d and div_2d to not call rshd/lshd unless needed + -- Fixed up etc/tune.c a tad + -- fixed up demo/demo.c to output comma-delimited results of timing + also fixed up timing demo to use a finer granularity for various functions + -- fixed up demo/demo.c testing to pause during testing so my Duron won't catch on fire + [stays around 31-35C during testing :-)] + +Feb 13th, 2003 +v0.13 -- tons of minor speed-ups in low level add, sub, mul_2 and div_2 which propagate + to other functions like mp_invmod, mp_div, etc... + -- Sped up mp_exptmod_fast by using new code to find R mod m [e.g. B^n mod m] + -- minor fixes + +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 + -- Sped up the montgomery reduction code quite a bit + -- fixed up demo so when building timing for the x86 it assumes ELF format now + +Jan 9th, 2003 +v0.10 -- Pekka Riikonen suggested fixes to the radix conversion code. + -- Added baseline montgomery and comba montgomery reductions, sped up exptmods + [to a point, see bn.h for MONTGOMERY_EXPT_CUTOFF] + +Jan 6th, 2003 +v0.09 -- Updated the manual to reflect recent changes. :-) + -- Added Jacobi function (mp_jacobi) to supplement the number theory side of the lib + -- Added a Mersenne prime finder demo in ./etc/mersenne.c + +Jan 2nd, 2003 +v0.08 -- Sped up the multipliers by moving the inner loop variables into a smaller scope + -- Corrected a bunch of small "warnings" + -- Added more comments + -- Made "mtest" be able to use /dev/random, /dev/urandom or stdin for RNG data + -- Corrected some bugs where error messages were potentially ignored + -- add etc/pprime.c program which makes numbers which are provably prime. + +Jan 1st, 2003 +v0.07 -- Removed alot of heap operations from core functions to speed them up + -- Added a root finding function [and mp_sqrt macro like from MPI] + -- Added more to manual + +Dec 31st, 2002 +v0.06 -- Sped up the s_mp_add, s_mp_sub which inturn sped up mp_invmod, mp_exptmod, etc... + -- Cleaned up the header a bit more + +Dec 30th, 2002 +v0.05 -- Builds with MSVC out of the box + -- Fixed a bug in mp_invmod w.r.t. even moduli + -- Made mp_toradix and mp_read_radix use char instead of unsigned char arrays + -- Fixed up exptmod to use fewer multiplications + -- Fixed up mp_init_size to use only one heap operation + -- Note there is a slight "off-by-one" bug in the library somewhere + without the padding (see the source for comment) the library + crashes in libtomcrypt. Anyways a reasonable workaround is to pad the + numbers which will always correct it since as the numbers grow the padding + will still be beyond the end of the number + -- Added more to the manual + +Dec 29th, 2002 +v0.04 -- Fixed a memory leak in mp_to_unsigned_bin + -- optimized invmod code + -- Fixed bug in mp_div + -- use exchange instead of copy for results + -- added a bit more to the manual + +Dec 27th, 2002 +v0.03 -- Sped up s_mp_mul_high_digs by not computing the carries of the lower digits + -- Fixed a bug where mp_set_int wouldn't zero the value first and set the used member. + -- fixed a bug in s_mp_mul_high_digs where the limit placed on the result digits was not calculated properly + -- fixed bugs in add/sub/mul/sqr_mod functions where if the modulus and dest were the same it wouldn't work + -- fixed a bug in mp_mod and mp_mod_d concerning negative inputs + -- mp_mul_d didn't preserve sign + -- Many many many many fixes + -- Works in LibTomCrypt now :-) + -- Added iterations to the timing demos... more accurate. + -- Tom needs a job. + +Dec 26th, 2002 +v0.02 -- Fixed a few "slips" in the manual. This is "LibTomMath" afterall :-) + -- Added mp_cmp_mag, mp_neg, mp_abs and mp_radix_size that were missing. + -- Sped up the fast [comba] multipliers more [yahoo!] + +Dec 25th,2002 +v0.01 -- Initial release. Gimme a break. + -- Todo list, + add details to manual [e.g. algorithms] + more comments in code + example programs diff --git a/demo/demo.c b/demo/demo.c new file mode 100644 index 0000000..8014ea8 --- /dev/null +++ b/demo/demo.c @@ -0,0 +1,785 @@ +#include + +#define TESTING + +#ifdef IOWNANATHLON +#include +#define SLEEP sleep(4) +#else +#define SLEEP +#endif + +#include "tommath.h" + +#ifdef TIMER +ulong64 _tt; + +#if defined(__i386__) || defined(_M_IX86) || defined(_M_AMD64) +/* RDTSC from Scott Duplichan */ +static ulong64 TIMFUNC (void) + { + #if defined __GNUC__ + #ifdef __i386__ + ulong64 a; + __asm__ __volatile__ ("rdtsc ":"=A" (a)); + return a; + #else /* gcc-IA64 version */ + unsigned long result; + __asm__ __volatile__("mov %0=ar.itc" : "=r"(result) :: "memory"); + while (__builtin_expect ((int) result == -1, 0)) + __asm__ __volatile__("mov %0=ar.itc" : "=r"(result) :: "memory"); + return result; + #endif + + // Microsoft and Intel Windows compilers + #elif defined _M_IX86 + __asm rdtsc + #elif defined _M_AMD64 + return __rdtsc (); + #elif defined _M_IA64 + #if defined __INTEL_COMPILER + #include + #endif + return __getReg (3116); + #else + #error need rdtsc function for this build + #endif + } +#else +#define TIMFUNC clock +#endif + +ulong64 rdtsc(void) { return TIMFUNC() - _tt; } +void reset(void) { _tt = TIMFUNC(); } + +#endif + +void ndraw(mp_int *a, char *name) +{ + char buf[4096]; + printf("%s: ", name); + mp_toradix(a, buf, 64); + printf("%s\n", buf); +} + +static void draw(mp_int *a) +{ + ndraw(a, ""); +} + + +unsigned long lfsr = 0xAAAAAAAAUL; + +int lbit(void) +{ + if (lfsr & 0x80000000UL) { + lfsr = ((lfsr << 1) ^ 0x8000001BUL) & 0xFFFFFFFFUL; + return 1; + } else { + lfsr <<= 1; + return 0; + } +} + +int myrng(unsigned char *dst, int len, void *dat) +{ + int x; + for (x = 0; x < len; x++) dst[x] = rand() & 0xFF; + return len; +} + + +#define DO2(x) x; x; +#define DO4(x) DO2(x); DO2(x); +#define DO8(x) DO4(x); DO4(x); +#define DO(x) DO8(x); DO8(x); + + char cmd[4096], buf[4096]; +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, + div2_n, mul2_n, add_d_n, sub_d_n, t; + unsigned rr; + int i, n, err, cnt, ix, old_kara_m, old_kara_s; + +#ifdef TIMER + ulong64 tt, CLK_PER_SEC; + FILE *log, *logb, *logc; +#endif + + mp_init(&a); + mp_init(&b); + mp_init(&c); + mp_init(&d); + mp_init(&e); + mp_init(&f); + + srand(time(NULL)); + +#ifdef TESTING + // test mp_get_int + printf("Testing: mp_get_int\n"); + for(i=0;i<1000;++i) { + t = (unsigned long)rand()*rand()+1; + mp_set_int(&a,t); + if (t!=mp_get_int(&a)) { + printf("mp_get_int() bad result!\n"); + return 1; + } + } + mp_set_int(&a,0); + if (mp_get_int(&a)!=0) + { printf("mp_get_int() bad result!\n"); + return 1; + } + mp_set_int(&a,0xffffffff); + if (mp_get_int(&a)!=0xffffffff) + { printf("mp_get_int() bad result!\n"); + return 1; + } + + // test mp_sqrt + printf("Testing: mp_sqrt\n"); + for (i=0;i<10000;++i) { + printf("%6d\r", i); fflush(stdout); + n = (rand()&15)+1; + mp_rand(&a,n); + if (mp_sqrt(&a,&b) != MP_OKAY) + { printf("mp_sqrt() error!\n"); + return 1; + } + mp_n_root(&a,2,&a); + if (mp_cmp_mag(&b,&a) != MP_EQ) + { printf("mp_sqrt() bad result!\n"); + return 1; + } + } + + printf("\nTesting: mp_is_square\n"); + for (i=0;i<100000;++i) { + printf("%6d\r", i); fflush(stdout); + + /* test mp_is_square false negatives */ + n = (rand()&7)+1; + mp_rand(&a,n); + mp_sqr(&a,&a); + if (mp_is_square(&a,&n)!=MP_OKAY) { + printf("fn:mp_is_square() error!\n"); + return 1; + } + if (n==0) { + printf("fn:mp_is_square() bad result!\n"); + return 1; + } + + /* test for false positives */ + mp_add_d(&a, 1, &a); + if (mp_is_square(&a,&n)!=MP_OKAY) { + printf("fp:mp_is_square() error!\n"); + return 1; + } + if (n==1) { + printf("fp:mp_is_square() bad result!\n"); + return 1; + } + + } + printf("\n\n"); +#endif + +#ifdef TESTING + /* test for size */ + for (ix = 16; ix < 512; ix++) { + printf("Testing (not safe-prime): %9d bits \r", ix); fflush(stdout); + err = mp_prime_random_ex(&a, 8, ix, (rand()&1)?LTM_PRIME_2MSB_OFF:LTM_PRIME_2MSB_ON, myrng, NULL); + if (err != MP_OKAY) { + printf("failed with err code %d\n", err); + return EXIT_FAILURE; + } + if (mp_count_bits(&a) != ix) { + printf("Prime is %d not %d bits!!!\n", mp_count_bits(&a), ix); + return EXIT_FAILURE; + } + } + + for (ix = 16; ix < 512; ix++) { + printf("Testing ( safe-prime): %9d bits \r", ix); fflush(stdout); + err = mp_prime_random_ex(&a, 8, ix, ((rand()&1)?LTM_PRIME_2MSB_OFF:LTM_PRIME_2MSB_ON)|LTM_PRIME_SAFE, myrng, NULL); + if (err != MP_OKAY) { + printf("failed with err code %d\n", err); + return EXIT_FAILURE; + } + if (mp_count_bits(&a) != ix) { + printf("Prime is %d not %d bits!!!\n", mp_count_bits(&a), ix); + return EXIT_FAILURE; + } + /* let's see if it's really a safe prime */ + mp_sub_d(&a, 1, &a); + mp_div_2(&a, &a); + mp_prime_is_prime(&a, 8, &cnt); + if (cnt != MP_YES) { + printf("sub is not prime!\n"); + return EXIT_FAILURE; + } + } + + printf("\n\n"); +#endif + +#ifdef TESTING + mp_read_radix(&a, "123456", 10); + mp_toradix_n(&a, buf, 10, 3); + printf("a == %s\n", buf); + mp_toradix_n(&a, buf, 10, 4); + printf("a == %s\n", buf); + mp_toradix_n(&a, buf, 10, 30); + printf("a == %s\n", buf); +#endif + + +#if 0 + for (;;) { + fgets(buf, sizeof(buf), stdin); + mp_read_radix(&a, buf, 10); + mp_prime_next_prime(&a, 5, 1); + mp_toradix(&a, buf, 10); + printf("%s, %lu\n", buf, a.dp[0] & 3); + } +#endif + +#if 0 +{ + mp_word aa, bb; + + for (;;) { + aa = abs(rand()) & MP_MASK; + bb = abs(rand()) & MP_MASK; + if (MULT(aa,bb) != (aa*bb)) { + printf("%llu * %llu == %llu or %llu?\n", aa, bb, (ulong64)MULT(aa,bb), (ulong64)(aa*bb)); + return 0; + } + } +} +#endif + +#ifdef TESTING + /* test mp_cnt_lsb */ + printf("testing mp_cnt_lsb...\n"); + mp_set(&a, 1); + for (ix = 0; ix < 1024; ix++) { + if (mp_cnt_lsb(&a) != ix) { + printf("Failed at %d, %d\n", ix, mp_cnt_lsb(&a)); + return 0; + } + mp_mul_2(&a, &a); + } +#endif + +/* test mp_reduce_2k */ +#ifdef TESTING + printf("Testing mp_reduce_2k...\n"); + for (cnt = 3; cnt <= 384; ++cnt) { + mp_digit tmp; + mp_2expt(&a, cnt); + mp_sub_d(&a, 2, &a); /* a = 2**cnt - 2 */ + + + printf("\nTesting %4d bits", cnt); + printf("(%d)", mp_reduce_is_2k(&a)); + mp_reduce_2k_setup(&a, &tmp); + printf("(%d)", tmp); + for (ix = 0; ix < 10000; ix++) { + if (!(ix & 127)) {printf("."); fflush(stdout); } + mp_rand(&b, (cnt/DIGIT_BIT + 1) * 2); + mp_copy(&c, &b); + mp_mod(&c, &a, &c); + mp_reduce_2k(&b, &a, 1); + if (mp_cmp(&c, &b)) { + printf("FAILED\n"); + exit(0); + } + } + } +#endif + + +/* test mp_div_3 */ +#ifdef TESTING + printf("Testing mp_div_3...\n"); + mp_set(&d, 3); + for (cnt = 0; cnt < 1000000; ) { + mp_digit r1, r2; + + if (!(++cnt & 127)) printf("%9d\r", cnt); + mp_rand(&a, abs(rand()) % 128 + 1); + mp_div(&a, &d, &b, &e); + mp_div_3(&a, &c, &r2); + + if (mp_cmp(&b, &c) || mp_cmp_d(&e, r2)) { + printf("\n\nmp_div_3 => Failure\n"); + } + } + printf("\n\nPassed div_3 testing\n"); +#endif + +/* test the DR reduction */ +#ifdef TESTING + printf("testing mp_dr_reduce...\n"); + for (cnt = 2; cnt < 128; cnt++) { + printf("%d digit modulus\n", cnt); + mp_grow(&a, cnt); + mp_zero(&a); + for (ix = 1; ix < cnt; ix++) { + a.dp[ix] = MP_MASK; + } + a.used = cnt; + mp_prime_next_prime(&a, 3, 0); + + mp_rand(&b, cnt - 1); + mp_copy(&b, &c); + + rr = 0; + do { + if (!(rr & 127)) { printf("%9lu\r", rr); fflush(stdout); } + mp_sqr(&b, &b); mp_add_d(&b, 1, &b); + mp_copy(&b, &c); + + mp_mod(&b, &a, &b); + mp_dr_reduce(&c, &a, (1< %9llu/sec, %9llu ticks\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt, tt); + fprintf(log, "%d %9llu\n", cnt*DIGIT_BIT, (((ulong64)rr)*CLK_PER_SEC)/tt); fflush(log); + } + fclose(log); + + log = fopen("logs/sub.log", "w"); + for (cnt = 8; cnt <= 128; cnt += 8) { + SLEEP; + mp_rand(&a, cnt); + mp_rand(&b, cnt); + reset(); + rr = 0; + do { + DO(mp_sub(&a,&b,&c)); + rr += 16; + } while (rdtsc() < (CLK_PER_SEC * 2)); + tt = rdtsc(); + printf("Subtracting\t\t%4d-bit => %9llu/sec, %9llu ticks\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt, tt); + fprintf(log, "%d %9llu\n", cnt*DIGIT_BIT, (((ulong64)rr)*CLK_PER_SEC)/tt); fflush(log); + } + fclose(log); + + /* do mult/square twice, first without karatsuba and second with */ +mult_test: + old_kara_m = KARATSUBA_MUL_CUTOFF; + old_kara_s = KARATSUBA_SQR_CUTOFF; + for (ix = 0; ix < 2; ix++) { + printf("With%s Karatsuba\n", (ix==0)?"out":""); + + KARATSUBA_MUL_CUTOFF = (ix==0)?9999:old_kara_m; + KARATSUBA_SQR_CUTOFF = (ix==0)?9999:old_kara_s; + + log = fopen((ix==0)?"logs/mult.log":"logs/mult_kara.log", "w"); + for (cnt = 32; cnt <= 288; cnt += 8) { + SLEEP; + mp_rand(&a, cnt); + mp_rand(&b, cnt); + reset(); + rr = 0; + do { + DO(mp_mul(&a, &b, &c)); + rr += 16; + } while (rdtsc() < (CLK_PER_SEC * 2)); + tt = rdtsc(); + printf("Multiplying\t%4d-bit => %9llu/sec, %9llu ticks\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt, tt); + fprintf(log, "%d %9llu\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt); fflush(log); + } + fclose(log); + + log = fopen((ix==0)?"logs/sqr.log":"logs/sqr_kara.log", "w"); + for (cnt = 32; cnt <= 288; cnt += 8) { + SLEEP; + mp_rand(&a, cnt); + reset(); + rr = 0; + do { + DO(mp_sqr(&a, &b)); + rr += 16; + } while (rdtsc() < (CLK_PER_SEC * 2)); + tt = rdtsc(); + printf("Squaring\t%4d-bit => %9llu/sec, %9llu ticks\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt, tt); + fprintf(log, "%d %9llu\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt); fflush(log); + } + fclose(log); + + } +expt_test: + { + char *primes[] = { + /* 2K moduli mersenne primes */ + "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", + "531137992816767098689588206552468627329593117727031923199444138200403559860852242739162502265229285668889329486246501015346579337652707239409519978766587351943831270835393219031728127", + "10407932194664399081925240327364085538615262247266704805319112350403608059673360298012239441732324184842421613954281007791383566248323464908139906605677320762924129509389220345773183349661583550472959420547689811211693677147548478866962501384438260291732348885311160828538416585028255604666224831890918801847068222203140521026698435488732958028878050869736186900714720710555703168729087", + "1475979915214180235084898622737381736312066145333169775147771216478570297878078949377407337049389289382748507531496480477281264838760259191814463365330269540496961201113430156902396093989090226259326935025281409614983499388222831448598601834318536230923772641390209490231836446899608210795482963763094236630945410832793769905399982457186322944729636418890623372171723742105636440368218459649632948538696905872650486914434637457507280441823676813517852099348660847172579408422316678097670224011990280170474894487426924742108823536808485072502240519452587542875349976558572670229633962575212637477897785501552646522609988869914013540483809865681250419497686697771007", + "259117086013202627776246767922441530941818887553125427303974923161874019266586362086201209516800483406550695241733194177441689509238807017410377709597512042313066624082916353517952311186154862265604547691127595848775610568757931191017711408826252153849035830401185072116424747461823031471398340229288074545677907941037288235820705892351068433882986888616658650280927692080339605869308790500409503709875902119018371991620994002568935113136548829739112656797303241986517250116412703509705427773477972349821676443446668383119322540099648994051790241624056519054483690809616061625743042361721863339415852426431208737266591962061753535748892894599629195183082621860853400937932839420261866586142503251450773096274235376822938649407127700846077124211823080804139298087057504713825264571448379371125032081826126566649084251699453951887789613650248405739378594599444335231188280123660406262468609212150349937584782292237144339628858485938215738821232393687046160677362909315071", + "190797007524439073807468042969529173669356994749940177394741882673528979787005053706368049835514900244303495954950709725762186311224148828811920216904542206960744666169364221195289538436845390250168663932838805192055137154390912666527533007309292687539092257043362517857366624699975402375462954490293259233303137330643531556539739921926201438606439020075174723029056838272505051571967594608350063404495977660656269020823960825567012344189908927956646011998057988548630107637380993519826582389781888135705408653045219655801758081251164080554609057468028203308718724654081055323215860189611391296030471108443146745671967766308925858547271507311563765171008318248647110097614890313562856541784154881743146033909602737947385055355960331855614540900081456378659068370317267696980001187750995491090350108417050917991562167972281070161305972518044872048331306383715094854938415738549894606070722584737978176686422134354526989443028353644037187375385397838259511833166416134323695660367676897722287918773420968982326089026150031515424165462111337527431154890666327374921446276833564519776797633875503548665093914556482031482248883127023777039667707976559857333357013727342079099064400455741830654320379350833236245819348824064783585692924881021978332974949906122664421376034687815350484991", + + /* DR moduli */ + "14059105607947488696282932836518693308967803494693489478439861164411992439598399594747002144074658928593502845729752797260025831423419686528151609940203368612079", + "101745825697019260773923519755878567461315282017759829107608914364075275235254395622580447400994175578963163918967182013639660669771108475957692810857098847138903161308502419410142185759152435680068435915159402496058513611411688900243039", + "736335108039604595805923406147184530889923370574768772191969612422073040099331944991573923112581267542507986451953227192970402893063850485730703075899286013451337291468249027691733891486704001513279827771740183629161065194874727962517148100775228363421083691764065477590823919364012917984605619526140821797602431", + "38564998830736521417281865696453025806593491967131023221754800625044118265468851210705360385717536794615180260494208076605798671660719333199513807806252394423283413430106003596332513246682903994829528690198205120921557533726473585751382193953592127439965050261476810842071573684505878854588706623484573925925903505747545471088867712185004135201289273405614415899438276535626346098904241020877974002916168099951885406379295536200413493190419727789712076165162175783", + "542189391331696172661670440619180536749994166415993334151601745392193484590296600979602378676624808129613777993466242203025054573692562689251250471628358318743978285860720148446448885701001277560572526947619392551574490839286458454994488665744991822837769918095117129546414124448777033941223565831420390846864429504774477949153794689948747680362212954278693335653935890352619041936727463717926744868338358149568368643403037768649616778526013610493696186055899318268339432671541328195724261329606699831016666359440874843103020666106568222401047720269951530296879490444224546654729111504346660859907296364097126834834235287147", + "1487259134814709264092032648525971038895865645148901180585340454985524155135260217788758027400478312256339496385275012465661575576202252063145698732079880294664220579764848767704076761853197216563262660046602703973050798218246170835962005598561669706844469447435461092542265792444947706769615695252256130901271870341005768912974433684521436211263358097522726462083917939091760026658925757076733484173202927141441492573799914240222628795405623953109131594523623353044898339481494120112723445689647986475279242446083151413667587008191682564376412347964146113898565886683139407005941383669325997475076910488086663256335689181157957571445067490187939553165903773554290260531009121879044170766615232300936675369451260747671432073394867530820527479172464106442450727640226503746586340279816318821395210726268291535648506190714616083163403189943334431056876038286530365757187367147446004855912033137386225053275419626102417236133948503", + "1095121115716677802856811290392395128588168592409109494900178008967955253005183831872715423151551999734857184538199864469605657805519106717529655044054833197687459782636297255219742994736751541815269727940751860670268774903340296040006114013971309257028332849679096824800250742691718610670812374272414086863715763724622797509437062518082383056050144624962776302147890521249477060215148275163688301275847155316042279405557632639366066847442861422164832655874655824221577849928863023018366835675399949740429332468186340518172487073360822220449055340582568461568645259954873303616953776393853174845132081121976327462740354930744487429617202585015510744298530101547706821590188733515880733527449780963163909830077616357506845523215289297624086914545378511082534229620116563260168494523906566709418166011112754529766183554579321224940951177394088465596712620076240067370589036924024728375076210477267488679008016579588696191194060127319035195370137160936882402244399699172017835144537488486396906144217720028992863941288217185353914991583400421682751000603596655790990815525126154394344641336397793791497068253936771017031980867706707490224041075826337383538651825493679503771934836094655802776331664261631740148281763487765852746577808019633679", + + /* generic unrestricted moduli */ + "17933601194860113372237070562165128350027320072176844226673287945873370751245439587792371960615073855669274087805055507977323024886880985062002853331424203", + "2893527720709661239493896562339544088620375736490408468011883030469939904368086092336458298221245707898933583190713188177399401852627749210994595974791782790253946539043962213027074922559572312141181787434278708783207966459019479487", + "347743159439876626079252796797422223177535447388206607607181663903045907591201940478223621722118173270898487582987137708656414344685816179420855160986340457973820182883508387588163122354089264395604796675278966117567294812714812796820596564876450716066283126720010859041484786529056457896367683122960411136319", + "47266428956356393164697365098120418976400602706072312735924071745438532218237979333351774907308168340693326687317443721193266215155735814510792148768576498491199122744351399489453533553203833318691678263241941706256996197460424029012419012634671862283532342656309677173602509498417976091509154360039893165037637034737020327399910409885798185771003505320583967737293415979917317338985837385734747478364242020380416892056650841470869294527543597349250299539682430605173321029026555546832473048600327036845781970289288898317888427517364945316709081173840186150794397479045034008257793436817683392375274635794835245695887", + "436463808505957768574894870394349739623346440601945961161254440072143298152040105676491048248110146278752857839930515766167441407021501229924721335644557342265864606569000117714935185566842453630868849121480179691838399545644365571106757731317371758557990781880691336695584799313313687287468894148823761785582982549586183756806449017542622267874275103877481475534991201849912222670102069951687572917937634467778042874315463238062009202992087620963771759666448266532858079402669920025224220613419441069718482837399612644978839925207109870840278194042158748845445131729137117098529028886770063736487420613144045836803985635654192482395882603511950547826439092832800532152534003936926017612446606135655146445620623395788978726744728503058670046885876251527122350275750995227", + "11424167473351836398078306042624362277956429440521137061889702611766348760692206243140413411077394583180726863277012016602279290144126785129569474909173584789822341986742719230331946072730319555984484911716797058875905400999504305877245849119687509023232790273637466821052576859232452982061831009770786031785669030271542286603956118755585683996118896215213488875253101894663403069677745948305893849505434201763745232895780711972432011344857521691017896316861403206449421332243658855453435784006517202894181640562433575390821384210960117518650374602256601091379644034244332285065935413233557998331562749140202965844219336298970011513882564935538704289446968322281451907487362046511461221329799897350993370560697505809686438782036235372137015731304779072430260986460269894522159103008260495503005267165927542949439526272736586626709581721032189532726389643625590680105784844246152702670169304203783072275089194754889511973916207", + "1214855636816562637502584060163403830270705000634713483015101384881871978446801224798536155406895823305035467591632531067547890948695117172076954220727075688048751022421198712032848890056357845974246560748347918630050853933697792254955890439720297560693579400297062396904306270145886830719309296352765295712183040773146419022875165382778007040109957609739589875590885701126197906063620133954893216612678838507540777138437797705602453719559017633986486649523611975865005712371194067612263330335590526176087004421363598470302731349138773205901447704682181517904064735636518462452242791676541725292378925568296858010151852326316777511935037531017413910506921922450666933202278489024521263798482237150056835746454842662048692127173834433089016107854491097456725016327709663199738238442164843147132789153725513257167915555162094970853584447993125488607696008169807374736711297007473812256272245489405898470297178738029484459690836250560495461579533254473316340608217876781986188705928270735695752830825527963838355419762516246028680280988020401914551825487349990306976304093109384451438813251211051597392127491464898797406789175453067960072008590614886532333015881171367104445044718144312416815712216611576221546455968770801413440778423979", + NULL + }; + log = fopen("logs/expt.log", "w"); + logb = fopen("logs/expt_dr.log", "w"); + logc = fopen("logs/expt_2k.log", "w"); + for (n = 0; primes[n]; n++) { + SLEEP; + mp_read_radix(&a, primes[n], 10); + mp_zero(&b); + for (rr = 0; rr < mp_count_bits(&a); rr++) { + mp_mul_2(&b, &b); + b.dp[0] |= lbit(); + b.used += 1; + } + mp_sub_d(&a, 1, &c); + mp_mod(&b, &c, &b); + mp_set(&c, 3); + reset(); + rr = 0; + do { + DO(mp_exptmod(&c, &b, &a, &d)); + rr += 16; + } while (rdtsc() < (CLK_PER_SEC * 2)); + tt = rdtsc(); + mp_sub_d(&a, 1, &e); + mp_sub(&e, &b, &b); + mp_exptmod(&c, &b, &a, &e); /* c^(p-1-b) mod a */ + mp_mulmod(&e, &d, &a, &d); /* c^b * c^(p-1-b) == c^p-1 == 1 */ + if (mp_cmp_d(&d, 1)) { + printf("Different (%d)!!!\n", mp_count_bits(&a)); + draw(&d); + exit(0); + } + printf("Exponentiating\t%4d-bit => %9llu/sec, %9llu ticks\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt, tt); + fprintf((n < 6) ? logc : (n < 13) ? logb : log, "%d %9llu\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt); + } + } + fclose(log); + fclose(logb); + fclose(logc); + + log = fopen("logs/invmod.log", "w"); + for (cnt = 4; cnt <= 128; cnt += 4) { + SLEEP; + mp_rand(&a, cnt); + mp_rand(&b, cnt); + + do { + mp_add_d(&b, 1, &b); + mp_gcd(&a, &b, &c); + } while (mp_cmp_d(&c, 1) != MP_EQ); + + reset(); + rr = 0; + do { + DO(mp_invmod(&b, &a, &c)); + rr += 16; + } while (rdtsc() < (CLK_PER_SEC * 2)); + tt = rdtsc(); + mp_mulmod(&b, &c, &a, &d); + if (mp_cmp_d(&d, 1) != MP_EQ) { + printf("Failed to invert\n"); + return 0; + } + printf("Inverting mod\t%4d-bit => %9llu/sec, %9llu ticks\n", mp_count_bits(&a), (((ulong64)rr)*CLK_PER_SEC)/tt, tt); + fprintf(log, "%d %9llu\n", cnt*DIGIT_BIT, (((ulong64)rr)*CLK_PER_SEC)/tt); + } + fclose(log); + + return 0; + +#endif + + div2_n = mul2_n = inv_n = expt_n = lcm_n = gcd_n = add_n = + sub_n = mul_n = div_n = sqr_n = mul2d_n = div2d_n = cnt = add_d_n = sub_d_n= 0; + + /* force KARA and TOOM to enable despite cutoffs */ + KARATSUBA_SQR_CUTOFF = KARATSUBA_MUL_CUTOFF = 110; + TOOM_SQR_CUTOFF = TOOM_MUL_CUTOFF = 150; + + for (;;) { + /* randomly clear and re-init one variable, this has the affect of triming the alloc space */ + switch (abs(rand()) % 7) { + case 0: mp_clear(&a); mp_init(&a); break; + case 1: mp_clear(&b); mp_init(&b); break; + case 2: mp_clear(&c); mp_init(&c); break; + case 3: mp_clear(&d); mp_init(&d); break; + case 4: mp_clear(&e); mp_init(&e); break; + case 5: mp_clear(&f); mp_init(&f); break; + case 6: break; /* don't clear any */ + } + + + printf("%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu/%4lu ", add_n, sub_n, mul_n, div_n, sqr_n, mul2d_n, div2d_n, gcd_n, lcm_n, expt_n, inv_n, div2_n, mul2_n, add_d_n, sub_d_n); + fgets(cmd, 4095, stdin); + cmd[strlen(cmd)-1] = 0; + printf("%s ]\r",cmd); fflush(stdout); + if (!strcmp(cmd, "mul2d")) { ++mul2d_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); sscanf(buf, "%d", &rr); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + + mp_mul_2d(&a, rr, &a); + a.sign = b.sign; + if (mp_cmp(&a, &b) != MP_EQ) { + printf("mul2d failed, rr == %d\n",rr); + draw(&a); + draw(&b); + return 0; + } + } else if (!strcmp(cmd, "div2d")) { ++div2d_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); sscanf(buf, "%d", &rr); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + + mp_div_2d(&a, rr, &a, &e); + a.sign = b.sign; + if (a.used == b.used && a.used == 0) { a.sign = b.sign = MP_ZPOS; } + if (mp_cmp(&a, &b) != MP_EQ) { + printf("div2d failed, rr == %d\n",rr); + draw(&a); + draw(&b); + return 0; + } + } else if (!strcmp(cmd, "add")) { ++add_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + mp_copy(&a, &d); + mp_add(&d, &b, &d); + if (mp_cmp(&c, &d) != MP_EQ) { + printf("add %lu failure!\n", add_n); +draw(&a);draw(&b);draw(&c);draw(&d); + return 0; + } + + /* test the sign/unsigned storage functions */ + + rr = mp_signed_bin_size(&c); + mp_to_signed_bin(&c, (unsigned char *)cmd); + memset(cmd+rr, rand()&255, sizeof(cmd)-rr); + mp_read_signed_bin(&d, (unsigned char *)cmd, rr); + if (mp_cmp(&c, &d) != MP_EQ) { + printf("mp_signed_bin failure!\n"); + draw(&c); + draw(&d); + return 0; + } + + + rr = mp_unsigned_bin_size(&c); + mp_to_unsigned_bin(&c, (unsigned char *)cmd); + memset(cmd+rr, rand()&255, sizeof(cmd)-rr); + mp_read_unsigned_bin(&d, (unsigned char *)cmd, rr); + if (mp_cmp_mag(&c, &d) != MP_EQ) { + printf("mp_unsigned_bin failure!\n"); + draw(&c); + draw(&d); + return 0; + } + + } else if (!strcmp(cmd, "sub")) { ++sub_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + mp_copy(&a, &d); + mp_sub(&d, &b, &d); + if (mp_cmp(&c, &d) != MP_EQ) { + printf("sub %lu failure!\n", sub_n); +draw(&a);draw(&b);draw(&c);draw(&d); + return 0; + } + } else if (!strcmp(cmd, "mul")) { ++mul_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + mp_copy(&a, &d); + mp_mul(&d, &b, &d); + if (mp_cmp(&c, &d) != MP_EQ) { + printf("mul %lu failure!\n", mul_n); +draw(&a);draw(&b);draw(&c);draw(&d); + return 0; + } + } else if (!strcmp(cmd, "div")) { ++div_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&d, buf, 64); + + mp_div(&a, &b, &e, &f); + if (mp_cmp(&c, &e) != MP_EQ || mp_cmp(&d, &f) != MP_EQ) { + printf("div %lu failure!\n", div_n); +draw(&a);draw(&b);draw(&c);draw(&d); draw(&e); draw(&f); + return 0; + } + + } else if (!strcmp(cmd, "sqr")) { ++sqr_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + mp_copy(&a, &c); + mp_sqr(&c, &c); + if (mp_cmp(&b, &c) != MP_EQ) { + printf("sqr %lu failure!\n", sqr_n); +draw(&a);draw(&b);draw(&c); + return 0; + } + } else if (!strcmp(cmd, "gcd")) { ++gcd_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + mp_copy(&a, &d); + mp_gcd(&d, &b, &d); + d.sign = c.sign; + if (mp_cmp(&c, &d) != MP_EQ) { + printf("gcd %lu failure!\n", gcd_n); +draw(&a);draw(&b);draw(&c);draw(&d); + return 0; + } + } else if (!strcmp(cmd, "lcm")) { ++lcm_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + mp_copy(&a, &d); + mp_lcm(&d, &b, &d); + d.sign = c.sign; + if (mp_cmp(&c, &d) != MP_EQ) { + printf("lcm %lu failure!\n", lcm_n); + draw(&a);draw(&b);draw(&c);draw(&d); + return 0; + } + } else if (!strcmp(cmd, "expt")) { ++expt_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&d, buf, 64); + mp_copy(&a, &e); + mp_exptmod(&e, &b, &c, &e); + if (mp_cmp(&d, &e) != MP_EQ) { + printf("expt %lu failure!\n", expt_n); + draw(&a);draw(&b);draw(&c);draw(&d); draw(&e); + return 0; + } + } else if (!strcmp(cmd, "invmod")) { ++inv_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&c, buf, 64); + mp_invmod(&a, &b, &d); + mp_mulmod(&d,&a,&b,&e); + if (mp_cmp_d(&e, 1) != MP_EQ) { + printf("inv [wrong value from MPI?!] failure\n"); + draw(&a);draw(&b);draw(&c);draw(&d); + mp_gcd(&a, &b, &e); + draw(&e); + return 0; + } + + } else if (!strcmp(cmd, "div2")) { ++div2_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + mp_div_2(&a, &c); + if (mp_cmp(&c, &b) != MP_EQ) { + printf("div_2 %lu failure\n", div2_n); + draw(&a); + draw(&b); + draw(&c); + return 0; + } + } else if (!strcmp(cmd, "mul2")) { ++mul2_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + mp_mul_2(&a, &c); + if (mp_cmp(&c, &b) != MP_EQ) { + printf("mul_2 %lu failure\n", mul2_n); + draw(&a); + draw(&b); + draw(&c); + return 0; + } + } else if (!strcmp(cmd, "add_d")) { ++add_d_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); sscanf(buf, "%d", &ix); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + mp_add_d(&a, ix, &c); + if (mp_cmp(&b, &c) != MP_EQ) { + printf("add_d %lu failure\n", add_d_n); + draw(&a); + draw(&b); + draw(&c); + printf("d == %d\n", ix); + return 0; + } + } else if (!strcmp(cmd, "sub_d")) { ++sub_d_n; + fgets(buf, 4095, stdin); mp_read_radix(&a, buf, 64); + fgets(buf, 4095, stdin); sscanf(buf, "%d", &ix); + fgets(buf, 4095, stdin); mp_read_radix(&b, buf, 64); + mp_sub_d(&a, ix, &c); + if (mp_cmp(&b, &c) != MP_EQ) { + printf("sub_d %lu failure\n", sub_d_n); + draw(&a); + draw(&b); + draw(&c); + printf("d == %d\n", ix); + return 0; + } + } + } + return 0; +} + diff --git a/etc/2kprime.1 b/etc/2kprime.1 new file mode 100644 index 0000000..c41ded1 --- /dev/null +++ b/etc/2kprime.1 @@ -0,0 +1,2 @@ +256-bits (k = 36113) = 115792089237316195423570985008687907853269984665640564039457584007913129603823 +512-bits (k = 38117) = 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006045979 diff --git a/etc/2kprime.c b/etc/2kprime.c new file mode 100644 index 0000000..d48b83e --- /dev/null +++ b/etc/2kprime.c @@ -0,0 +1,80 @@ +/* Makes safe primes of a 2k nature */ +#include +#include + +int sizes[] = {256, 512, 768, 1024, 1536, 2048, 3072, 4096}; + +int main(void) +{ + char buf[2000]; + int x, y; + mp_int q, p; + FILE *out; + clock_t t1; + mp_digit z; + + mp_init_multi(&q, &p, NULL); + + out = fopen("2kprime.1", "w"); + for (x = 0; x < (int)(sizeof(sizes) / sizeof(sizes[0])); x++) { + top: + mp_2expt(&q, sizes[x]); + mp_add_d(&q, 3, &q); + z = -3; + + t1 = clock(); + for(;;) { + mp_sub_d(&q, 4, &q); + z += 4; + + if (z > MP_MASK) { + printf("No primes of size %d found\n", sizes[x]); + break; + } + + if (clock() - t1 > CLOCKS_PER_SEC) { + printf("."); fflush(stdout); +// sleep((clock() - t1 + CLOCKS_PER_SEC/2)/CLOCKS_PER_SEC); + t1 = clock(); + } + + /* quick test on q */ + mp_prime_is_prime(&q, 1, &y); + if (y == 0) { + continue; + } + + /* find (q-1)/2 */ + mp_sub_d(&q, 1, &p); + mp_div_2(&p, &p); + mp_prime_is_prime(&p, 3, &y); + if (y == 0) { + continue; + } + + /* test on q */ + mp_prime_is_prime(&q, 3, &y); + if (y == 0) { + continue; + } + + break; + } + + if (y == 0) { + ++sizes[x]; + goto top; + } + + mp_toradix(&q, buf, 10); + printf("\n\n%d-bits (k = %lu) = %s\n", sizes[x], z, buf); + fprintf(out, "%d-bits (k = %lu) = %s\n", sizes[x], z, buf); fflush(out); + } + + return 0; +} + + + + + diff --git a/etc/drprime.c b/etc/drprime.c new file mode 100644 index 0000000..0ab8ea6 --- /dev/null +++ b/etc/drprime.c @@ -0,0 +1,60 @@ +/* Makes safe primes of a DR nature */ +#include + +int sizes[] = { 1+256/DIGIT_BIT, 1+512/DIGIT_BIT, 1+768/DIGIT_BIT, 1+1024/DIGIT_BIT, 1+2048/DIGIT_BIT, 1+4096/DIGIT_BIT }; +int main(void) +{ + int res, x, y; + char buf[4096]; + FILE *out; + mp_int a, b; + + mp_init(&a); + mp_init(&b); + + out = fopen("drprimes.txt", "w"); + for (x = 0; x < (int)(sizeof(sizes)/sizeof(sizes[0])); x++) { + top: + printf("Seeking a %d-bit safe prime\n", sizes[x] * DIGIT_BIT); + mp_grow(&a, sizes[x]); + mp_zero(&a); + for (y = 1; y < sizes[x]; y++) { + a.dp[y] = MP_MASK; + } + + /* make a DR modulus */ + a.dp[0] = -1; + a.used = sizes[x]; + + /* now loop */ + res = 0; + for (;;) { + a.dp[0] += 4; + if (a.dp[0] >= MP_MASK) break; + mp_prime_is_prime(&a, 1, &res); + if (res == 0) continue; + printf("."); fflush(stdout); + mp_sub_d(&a, 1, &b); + mp_div_2(&b, &b); + mp_prime_is_prime(&b, 3, &res); + if (res == 0) continue; + mp_prime_is_prime(&a, 3, &res); + if (res == 1) break; + } + + if (res != 1) { + printf("Error not DR modulus\n"); sizes[x] += 1; goto top; + } else { + mp_toradix(&a, buf, 10); + printf("\n\np == %s\n\n", buf); + fprintf(out, "%d-bit prime:\np == %s\n\n", mp_count_bits(&a), buf); fflush(out); + } + } + fclose(out); + + mp_clear(&a); + mp_clear(&b); + + return 0; +} + diff --git a/etc/drprimes.28 b/etc/drprimes.28 new file mode 100644 index 0000000..9d438ad --- /dev/null +++ b/etc/drprimes.28 @@ -0,0 +1,25 @@ +DR safe primes for 28-bit digits. + +224-bit prime: +p == 26959946667150639794667015087019630673637144422540572481103341844143 + +532-bit prime: +p == 14059105607947488696282932836518693308967803494693489478439861164411992439598399594747002144074658928593502845729752797260025831423419686528151609940203368691747 + +784-bit prime: +p == 101745825697019260773923519755878567461315282017759829107608914364075275235254395622580447400994175578963163918967182013639660669771108475957692810857098847138903161308502419410142185759152435680068435915159402496058513611411688900243039 + +1036-bit prime: +p == 736335108039604595805923406147184530889923370574768772191969612422073040099331944991573923112581267542507986451953227192970402893063850485730703075899286013451337291468249027691733891486704001513279827771740183629161065194874727962517148100775228363421083691764065477590823919364012917984605619526140821798437127 + +1540-bit prime: +p == 38564998830736521417281865696453025806593491967131023221754800625044118265468851210705360385717536794615180260494208076605798671660719333199513807806252394423283413430106003596332513246682903994829528690198205120921557533726473585751382193953592127439965050261476810842071573684505878854588706623484573925925903505747545471088867712185004135201289273405614415899438276535626346098904241020877974002916168099951885406379295536200413493190419727789712076165162175783 + +2072-bit prime: +p == 542189391331696172661670440619180536749994166415993334151601745392193484590296600979602378676624808129613777993466242203025054573692562689251250471628358318743978285860720148446448885701001277560572526947619392551574490839286458454994488665744991822837769918095117129546414124448777033941223565831420390846864429504774477949153794689948747680362212954278693335653935890352619041936727463717926744868338358149568368643403037768649616778526013610493696186055899318268339432671541328195724261329606699831016666359440874843103020666106568222401047720269951530296879490444224546654729111504346660859907296364097126834834235287147 + +3080-bit prime: +p == 1487259134814709264092032648525971038895865645148901180585340454985524155135260217788758027400478312256339496385275012465661575576202252063145698732079880294664220579764848767704076761853197216563262660046602703973050798218246170835962005598561669706844469447435461092542265792444947706769615695252256130901271870341005768912974433684521436211263358097522726462083917939091760026658925757076733484173202927141441492573799914240222628795405623953109131594523623353044898339481494120112723445689647986475279242446083151413667587008191682564376412347964146113898565886683139407005941383669325997475076910488086663256335689181157957571445067490187939553165903773554290260531009121879044170766615232300936675369451260747671432073394867530820527479172464106442450727640226503746586340279816318821395210726268291535648506190714616083163403189943334431056876038286530365757187367147446004855912033137386225053275419626102417236133948503 + +4116-bit prime: +p == 1095121115716677802856811290392395128588168592409109494900178008967955253005183831872715423151551999734857184538199864469605657805519106717529655044054833197687459782636297255219742994736751541815269727940751860670268774903340296040006114013971309257028332849679096824800250742691718610670812374272414086863715763724622797509437062518082383056050144624962776302147890521249477060215148275163688301275847155316042279405557632639366066847442861422164832655874655824221577849928863023018366835675399949740429332468186340518172487073360822220449055340582568461568645259954873303616953776393853174845132081121976327462740354930744487429617202585015510744298530101547706821590188733515880733527449780963163909830077616357506845523215289297624086914545378511082534229620116563260168494523906566709418166011112754529766183554579321224940951177394088465596712620076240067370589036924024728375076210477267488679008016579588696191194060127319035195370137160936882402244399699172017835144537488486396906144217720028992863941288217185353914991583400421682751000603596655790990815525126154394344641336397793791497068253936771017031980867706707490224041075826337383538651825493679503771934836094655802776331664261631740148281763487765852746577808019633679 diff --git a/etc/drprimes.txt b/etc/drprimes.txt new file mode 100644 index 0000000..2c887ea --- /dev/null +++ b/etc/drprimes.txt @@ -0,0 +1,6 @@ +280-bit prime: +p == 1942668892225729070919461906823518906642406839052139521251812409738904285204940164839 + +532-bit prime: +p == 14059105607947488696282932836518693308967803494693489478439861164411992439598399594747002144074658928593502845729752797260025831423419686528151609940203368691747 + diff --git a/etc/makefile b/etc/makefile new file mode 100644 index 0000000..98ddb1c --- /dev/null +++ b/etc/makefile @@ -0,0 +1,49 @@ +CFLAGS += -Wall -W -Wshadow -O3 -fomit-frame-pointer -funroll-loops -I../ + +# default lib name (requires install with root) +# LIBNAME=-ltommath + +# libname when you can't install the lib with install +LIBNAME=../libtommath.a + +#provable primes +pprime: pprime.o + $(CC) pprime.o $(LIBNAME) -o pprime + +# portable [well requires clock()] tuning app +tune: tune.o + $(CC) tune.o $(LIBNAME) -o tune + +# same app but using RDTSC for higher precision [requires 80586+], coff based gcc installs [e.g. ming, cygwin, djgpp] +tune86: tune.c + nasm -f coff timer.asm + $(CC) -DX86_TIMER $(CFLAGS) tune.c timer.o $(LIBNAME) -o tune86 + +# for cygwin +tune86c: tune.c + nasm -f gnuwin32 timer.asm + $(CC) -DX86_TIMER $(CFLAGS) tune.c timer.o $(LIBNAME) -o tune86 + +#make tune86 for linux or any ELF format +tune86l: tune.c + nasm -f elf -DUSE_ELF timer.asm + $(CC) -DX86_TIMER $(CFLAGS) tune.c timer.o $(LIBNAME) -o tune86l + +# spits out mersenne primes +mersenne: mersenne.o + $(CC) mersenne.o $(LIBNAME) -o mersenne + +# fines DR safe primes for the given config +drprime: drprime.o + $(CC) drprime.o $(LIBNAME) -o drprime + +# fines 2k safe primes for the given config +2kprime: 2kprime.o + $(CC) 2kprime.o $(LIBNAME) -o 2kprime + +mont: mont.o + $(CC) mont.o $(LIBNAME) -o mont + + +clean: + rm -f *.log *.o *.obj *.exe pprime tune mersenne drprime tune86 tune86l mont 2kprime pprime.dat diff --git a/etc/makefile.msvc b/etc/makefile.msvc new file mode 100644 index 0000000..2833372 --- /dev/null +++ b/etc/makefile.msvc @@ -0,0 +1,23 @@ +#MSVC Makefile +# +#Tom St Denis + +CFLAGS = /I../ /Ox /DWIN32 /W3 + +pprime: pprime.obj + cl pprime.obj ../tommath.lib + +mersenne: mersenne.obj + cl mersenne.obj ../tommath.lib + +tune: tune.obj + cl tune.obj ../tommath.lib + +mont: mont.obj + cl mont.obj ../tommath.lib + +drprime: drprime.obj + cl drprime.obj ../tommath.lib + +2kprime: 2kprime.obj + cl 2kprime.obj ../tommath.lib diff --git a/etc/mersenne.c b/etc/mersenne.c new file mode 100644 index 0000000..da6c111 --- /dev/null +++ b/etc/mersenne.c @@ -0,0 +1,140 @@ +/* Finds Mersenne primes using the Lucas-Lehmer test + * + * Tom St Denis, tomstdenis@iahu.ca + */ +#include +#include + +int +is_mersenne (long s, int *pp) +{ + mp_int n, u; + int res, k; + + *pp = 0; + + if ((res = mp_init (&n)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&u)) != MP_OKAY) { + goto __N; + } + + /* n = 2^s - 1 */ + if ((res = mp_2expt(&n, s)) != MP_OKAY) { + goto __MU; + } + if ((res = mp_sub_d (&n, 1, &n)) != MP_OKAY) { + goto __MU; + } + + /* 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 */ + while (u.sign == MP_NEG) { + if ((res = mp_add (&u, &n, &u)) != MP_OKAY) { + goto __MU; + } + } + + /* reduce */ + if ((res = mp_reduce_2k (&u, &n, 1)) != MP_OKAY) { + goto __MU; + } + } + + /* if u == 0 then its prime */ + if (mp_iszero (&u) == 1) { + mp_prime_is_prime(&n, 8, pp); + if (*pp != 1) printf("FAILURE\n"); + } + + res = MP_OKAY; +__MU:mp_clear (&u); +__N:mp_clear (&n); + return res; +} + +/* square root of a long < 65536 */ +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; +} + +/* is the long prime by brute force */ +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; +} + + +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/mont.c b/etc/mont.c new file mode 100644 index 0000000..dbf1735 --- /dev/null +++ b/etc/mont.c @@ -0,0 +1,46 @@ +/* tests the montgomery routines */ +#include + +int main(void) +{ + mp_int modulus, R, p, pp; + mp_digit mp; + long x, y; + + srand(time(NULL)); + mp_init_multi(&modulus, &R, &p, &pp, NULL); + + /* loop through various sizes */ + for (x = 4; x < 256; x++) { + printf("DIGITS == %3ld...", x); fflush(stdout); + + /* make up the odd modulus */ + mp_rand(&modulus, x); + modulus.dp[0] |= 1; + + /* now find the R value */ + mp_montgomery_calc_normalization(&R, &modulus); + mp_montgomery_setup(&modulus, &mp); + + /* now run through a bunch tests */ + for (y = 0; y < 1000; y++) { + mp_rand(&p, x/2); /* p = random */ + mp_mul(&p, &R, &pp); /* pp = R * p */ + mp_montgomery_reduce(&pp, &modulus, mp); + + /* should be equal to p */ + if (mp_cmp(&pp, &p) != MP_EQ) { + printf("FAILURE!\n"); + exit(-1); + } + } + printf("PASSED\n"); + } + + return 0; +} + + + + + diff --git a/etc/pprime.c b/etc/pprime.c new file mode 100644 index 0000000..cccb748 --- /dev/null +++ b/etc/pprime.c @@ -0,0 +1,396 @@ +/* Generates provable primes + * + * See http://iahu.ca:8080/papers/pp.pdf for more info. + * + * Tom St Denis, tomstdenis@iahu.ca, http://tom.iahu.ca + */ +#include +#include "tommath.h" + +int n_prime; +FILE *primes; + +/* fast square root */ +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; +} + + +/* generates a prime digit */ +static void gen_prime (void) +{ + mp_digit r, x, y, next; + FILE *out; + + out = fopen("pprime.dat", "wb"); + + /* write first set of primes */ + r = 3; fwrite(&r, 1, sizeof(mp_digit), out); + r = 5; fwrite(&r, 1, sizeof(mp_digit), out); + r = 7; fwrite(&r, 1, sizeof(mp_digit), out); + r = 11; fwrite(&r, 1, sizeof(mp_digit), out); + r = 13; fwrite(&r, 1, sizeof(mp_digit), out); + r = 17; fwrite(&r, 1, sizeof(mp_digit), out); + r = 19; fwrite(&r, 1, sizeof(mp_digit), out); + r = 23; fwrite(&r, 1, sizeof(mp_digit), out); + r = 29; fwrite(&r, 1, sizeof(mp_digit), out); + r = 31; fwrite(&r, 1, sizeof(mp_digit), out); + + /* get square root, since if 'r' is composite its factors must be < than this */ + y = i_sqrt (r); + next = (y + 1) * (y + 1); + + for (;;) { + do { + r += 2; /* next candidate */ + r &= MP_MASK; + if (r < 31) break; + + /* 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; + } + 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); + if (r > 31) { fwrite(&r, 1, sizeof(mp_digit), out); printf("%9d\r", r); fflush(stdout); } + if (r < 31) break; + } + + fclose(out); +} + +void load_tab(void) +{ + primes = fopen("pprime.dat", "rb"); + if (primes == NULL) { + gen_prime(); + primes = fopen("pprime.dat", "rb"); + } + fseek(primes, 0, SEEK_END); + n_prime = ftell(primes) / sizeof(mp_digit); +} + +mp_digit prime_digit(void) +{ + int n; + mp_digit d; + + n = abs(rand()) % n_prime; + fseek(primes, n * sizeof(mp_digit), SEEK_SET); + fread(&d, 1, sizeof(mp_digit), primes); + return d; +} + + +/* makes a prime of at least k bits */ +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; + } + + 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) { + fprintf (stderr, "prime has %4d bits left\r", k - mp_count_bits (&a)); + fflush (stderr); + 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; + + /* 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; + +{ + char buf[4096]; + + mp_toradix(&n, buf, 10); + printf("Certificate of primality for:\n%s\n\n", buf); + mp_toradix(&a, buf, 10); + printf("A == \n%s\n\n", buf); + mp_toradix(&b, buf, 10); + printf("B == \n%s\n\nG == %d\n", buf, bases[ii]); + printf("----------------------------------------------------------------\n"); +} + + /* 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_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; +} + + +int +main (void) +{ + mp_int p, q; + char buf[4096]; + int k, li; + clock_t t1; + + srand (time (NULL)); + load_tab(); + + 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/prime.1024 b/etc/prime.1024 new file mode 100644 index 0000000..5636e2d --- /dev/null +++ b/etc/prime.1024 @@ -0,0 +1,414 @@ +Enter # of bits: +Enter number of bases to try (1 to 8): +Certificate of primality for: +36360080703173363 + +A == +89963569 + +B == +202082249 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +4851595597739856136987139 + +A == +36360080703173363 + +B == +66715963 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +19550639734462621430325731591027 + +A == +4851595597739856136987139 + +B == +2014867 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +10409036141344317165691858509923818734539 + +A == +19550639734462621430325731591027 + +B == +266207047 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +1049829549988285012736475602118094726647504414203 + +A == +10409036141344317165691858509923818734539 + +B == +50428759 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +77194737385528288387712399596835459931920358844586615003 + +A == +1049829549988285012736475602118094726647504414203 + +B == +36765367 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +35663756695365208574443215955488689578374232732893628896541201763 + +A == +77194737385528288387712399596835459931920358844586615003 + +B == +230998627 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +16711831463502165169495622246023119698415848120292671294127567620396469803 + +A == +35663756695365208574443215955488689578374232732893628896541201763 + +B == +234297127 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +6163534781560285962890718925972249753147470953579266394395432475622345597103528739 + +A == +16711831463502165169495622246023119698415848120292671294127567620396469803 + +B == +184406323 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +814258256205243497704094951432575867360065658372158511036259934640748088306764553488803787 + +A == +6163534781560285962890718925972249753147470953579266394395432475622345597103528739 + +B == +66054487 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +176469695533271657902814176811660357049007467856432383037590673407330246967781451723764079581998187 + +A == +814258256205243497704094951432575867360065658372158511036259934640748088306764553488803787 + +B == +108362239 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +44924492859445516541759485198544012102424796403707253610035148063863073596051272171194806669756971406400419 + +A == +176469695533271657902814176811660357049007467856432383037590673407330246967781451723764079581998187 + +B == +127286707 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +20600996927219343383225424320134474929609459588323857796871086845924186191561749519858600696159932468024710985371059 + +A == +44924492859445516541759485198544012102424796403707253610035148063863073596051272171194806669756971406400419 + +B == +229284691 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +6295696427695493110141186605837397185848992307978456138112526915330347715236378041486547994708748840844217371233735072572979 + +A == +20600996927219343383225424320134474929609459588323857796871086845924186191561749519858600696159932468024710985371059 + +B == +152800771 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +3104984078042317488749073016454213579257792635142218294052134804187631661145261015102617582090263808696699966840735333252107678792123 + +A == +6295696427695493110141186605837397185848992307978456138112526915330347715236378041486547994708748840844217371233735072572979 + +B == +246595759 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +26405175827665701256325699315126705508919255051121452292124404943796947287968603975320562847910946802396632302209435206627913466015741799499 + +A == +3104984078042317488749073016454213579257792635142218294052134804187631661145261015102617582090263808696699966840735333252107678792123 + +B == +4252063 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +11122146237908413610034600609460545703591095894418599759742741406628055069007082998134905595800236452010905900391505454890446585211975124558601770163 + +A == +26405175827665701256325699315126705508919255051121452292124404943796947287968603975320562847910946802396632302209435206627913466015741799499 + +B == +210605419 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +1649861642047798890580354082088712649911849362201343649289384923147797960364736011515757482030049342943790127685185806092659832129486307035500638595572396187 + +A == +11122146237908413610034600609460545703591095894418599759742741406628055069007082998134905595800236452010905900391505454890446585211975124558601770163 + +B == +74170111 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +857983367126266717607389719637086684134462613006415859877666235955788392464081914127715967940968197765042399904117392707518175220864852816390004264107201177394565363 + +A == +1649861642047798890580354082088712649911849362201343649289384923147797960364736011515757482030049342943790127685185806092659832129486307035500638595572396187 + +B == +260016763 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +175995909353623703257072120479340610010337144085688850745292031336724691277374210929188442230237711063783727092685448718515661641054886101716698390145283196296702450566161283 + +A == +857983367126266717607389719637086684134462613006415859877666235955788392464081914127715967940968197765042399904117392707518175220864852816390004264107201177394565363 + +B == +102563707 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +48486002551155667224487059713350447239190772068092630563272168418880661006593537218144160068395218642353495339720640699721703003648144463556291315694787862009052641640656933232794283 + +A == +175995909353623703257072120479340610010337144085688850745292031336724691277374210929188442230237711063783727092685448718515661641054886101716698390145283196296702450566161283 + +B == +137747527 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +13156468011529105025061495011938518171328604045212410096476697450506055664012861932372156505805788068791146986282263016790631108386790291275939575123375304599622623328517354163964228279867403 + +A == +48486002551155667224487059713350447239190772068092630563272168418880661006593537218144160068395218642353495339720640699721703003648144463556291315694787862009052641640656933232794283 + +B == +135672847 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +6355194692790533601105154341731997464407930009404822926832136060319955058388106456084549316415200519472481147942263916585428906582726749131479465958107142228236909665306781538860053107680830113869123 + +A == +13156468011529105025061495011938518171328604045212410096476697450506055664012861932372156505805788068791146986282263016790631108386790291275939575123375304599622623328517354163964228279867403 + +B == +241523587 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +3157116676535430302794438027544146642863331358530722860333745617571010460905857862561870488000265751138954271040017454405707755458702044884023184574412221802502351503929935224995314581932097706874819348858083 + +A == +6355194692790533601105154341731997464407930009404822926832136060319955058388106456084549316415200519472481147942263916585428906582726749131479465958107142228236909665306781538860053107680830113869123 + +B == +248388667 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +390533129219992506725320633489467713907837370444962163378727819939092929448752905310115311180032249230394348337568973177802874166228132778126338883671958897238722734394783244237133367055422297736215754829839364158067 + +A == +3157116676535430302794438027544146642863331358530722860333745617571010460905857862561870488000265751138954271040017454405707755458702044884023184574412221802502351503929935224995314581932097706874819348858083 + +B == +61849651 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +48583654555070224891047847050732516652910250240135992225139515777200432486685999462997073444468380434359929499498804723793106565291183220444221080449740542884172281158126259373095216435009661050109711341419005972852770440739 + +A == +390533129219992506725320633489467713907837370444962163378727819939092929448752905310115311180032249230394348337568973177802874166228132778126338883671958897238722734394783244237133367055422297736215754829839364158067 + +B == +62201707 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +25733035251905120039135866524384525138869748427727001128764704499071378939227862068500633813538831598776578372709963673670934388213622433800015759585470542686333039614931682098922935087822950084908715298627996115185849260703525317419 + +A == +48583654555070224891047847050732516652910250240135992225139515777200432486685999462997073444468380434359929499498804723793106565291183220444221080449740542884172281158126259373095216435009661050109711341419005972852770440739 + +B == +264832231 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +2804594464939948901906623499531073917980499195397462605359913717827014360538186518540781517129548650937632008683280555602633122170458773895504894807182664540529077836857897972175530148107545939211339044386106111633510166695386323426241809387 + +A == +25733035251905120039135866524384525138869748427727001128764704499071378939227862068500633813538831598776578372709963673670934388213622433800015759585470542686333039614931682098922935087822950084908715298627996115185849260703525317419 + +B == +54494047 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +738136612083433720096707308165797114449914259256979340471077690416567237592465306112484843530074782721390528773594351482384711900456440808251196845265132086486672447136822046628407467459921823150600138073268385534588238548865012638209515923513516547 + +A == +2804594464939948901906623499531073917980499195397462605359913717827014360538186518540781517129548650937632008683280555602633122170458773895504894807182664540529077836857897972175530148107545939211339044386106111633510166695386323426241809387 + +B == +131594179 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +392847529056126766528615419937165193421166694172790666626558750047057558168124866940509180171236517681470100877687445134633784815352076138790217228749332398026714192707447855731679485746120589851992221508292976900578299504461333767437280988393026452846013683 + +A == +738136612083433720096707308165797114449914259256979340471077690416567237592465306112484843530074782721390528773594351482384711900456440808251196845265132086486672447136822046628407467459921823150600138073268385534588238548865012638209515923513516547 + +B == +266107603 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +168459393231883505975876919268398655632763956627405508859662408056221544310200546265681845397346956580604208064328814319465940958080244889692368602591598503944015835190587740756859842792554282496742843600573336023639256008687581291233481455395123454655488735304365627 + +A == +392847529056126766528615419937165193421166694172790666626558750047057558168124866940509180171236517681470100877687445134633784815352076138790217228749332398026714192707447855731679485746120589851992221508292976900578299504461333767437280988393026452846013683 + +B == +214408111 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +14865774288636941404884923981945833072113667565310054952177860608355263252462409554658728941191929400198053290113492910272458441655458514080123870132092365833472436407455910185221474386718838138135065780840839893113912689594815485706154461164071775481134379794909690501684643 + +A == +168459393231883505975876919268398655632763956627405508859662408056221544310200546265681845397346956580604208064328814319465940958080244889692368602591598503944015835190587740756859842792554282496742843600573336023639256008687581291233481455395123454655488735304365627 + +B == +44122723 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +1213301773203241614897109856134894783021668292000023984098824423682568173639394290886185366993108292039068940333907505157813934962357206131450244004178619265868614859794316361031904412926604138893775068853175215502104744339658944443630407632290152772487455298652998368296998719996019 + +A == +14865774288636941404884923981945833072113667565310054952177860608355263252462409554658728941191929400198053290113492910272458441655458514080123870132092365833472436407455910185221474386718838138135065780840839893113912689594815485706154461164071775481134379794909690501684643 + +B == +40808563 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +186935245989515158127969129347464851990429060640910951266513740972248428651109062997368144722015290092846666943896556191257222521203647606911446635194198213436423080005867489516421559330500722264446765608763224572386410155413161172707802334865729654109050873820610813855041667633843601286843 + +A == +1213301773203241614897109856134894783021668292000023984098824423682568173639394290886185366993108292039068940333907505157813934962357206131450244004178619265868614859794316361031904412926604138893775068853175215502104744339658944443630407632290152772487455298652998368296998719996019 + +B == +77035759 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +83142661079751490510739960019112406284111408348732592580459037404394946037094409915127399165633756159385609671956087845517678367844901424617866988187132480585966721962585586730693443536100138246516868613250009028187662080828012497191775172228832247706080044971423654632146928165751885302331924491683 + +A == +186935245989515158127969129347464851990429060640910951266513740972248428651109062997368144722015290092846666943896556191257222521203647606911446635194198213436423080005867489516421559330500722264446765608763224572386410155413161172707802334865729654109050873820610813855041667633843601286843 + +B == +222383587 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +3892354773803809855317742245039794448230625839512638747643814927766738642436392673485997449586432241626440927010641564064764336402368634186618250134234189066179771240232458249806850838490410473462391401438160528157981942499581634732706904411807195259620779379274017704050790865030808501633772117217899534443 + +A == +83142661079751490510739960019112406284111408348732592580459037404394946037094409915127399165633756159385609671956087845517678367844901424617866988187132480585966721962585586730693443536100138246516868613250009028187662080828012497191775172228832247706080044971423654632146928165751885302331924491683 + +B == +23407687 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +1663606652988091811284014366560171522582683318514519379924950390627250155440313691226744227787921928894551755219495501365555370027257568506349958010457682898612082048959464465369892842603765280317696116552850664773291371490339084156052244256635115997453399761029567033971998617303988376172539172702246575225837054723 + +A == +3892354773803809855317742245039794448230625839512638747643814927766738642436392673485997449586432241626440927010641564064764336402368634186618250134234189066179771240232458249806850838490410473462391401438160528157981942499581634732706904411807195259620779379274017704050790865030808501633772117217899534443 + +B == +213701827 + +G == 2 +---------------------------------------------------------------- + + +Took 33057 ticks, 1048 bits +P == 1663606652988091811284014366560171522582683318514519379924950390627250155440313691226744227787921928894551755219495501365555370027257568506349958010457682898612082048959464465369892842603765280317696116552850664773291371490339084156052244256635115997453399761029567033971998617303988376172539172702246575225837054723 +Q == 3892354773803809855317742245039794448230625839512638747643814927766738642436392673485997449586432241626440927010641564064764336402368634186618250134234189066179771240232458249806850838490410473462391401438160528157981942499581634732706904411807195259620779379274017704050790865030808501633772117217899534443 diff --git a/etc/prime.512 b/etc/prime.512 new file mode 100644 index 0000000..cb6ec30 --- /dev/null +++ b/etc/prime.512 @@ -0,0 +1,205 @@ +Enter # of bits: +Enter number of bases to try (1 to 8): +Certificate of primality for: +85933926807634727 + +A == +253758023 + +B == +169322581 + +G == 5 +---------------------------------------------------------------- +Certificate of primality for: +23930198825086241462113799 + +A == +85933926807634727 + +B == +139236037 + +G == 11 +---------------------------------------------------------------- +Certificate of primality for: +6401844647261612602378676572510019 + +A == +23930198825086241462113799 + +B == +133760791 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +269731366027728777712034888684015329354259 + +A == +6401844647261612602378676572510019 + +B == +21066691 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +37942338209025571690075025099189467992329684223707 + +A == +269731366027728777712034888684015329354259 + +B == +70333567 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +15306904714258982484473490774101705363308327436988160248323 + +A == +37942338209025571690075025099189467992329684223707 + +B == +201712723 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +1616744757018513392810355191503853040357155275733333124624513530099 + +A == +15306904714258982484473490774101705363308327436988160248323 + +B == +52810963 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +464222094814208047161771036072622485188658077940154689939306386289983787983 + +A == +1616744757018513392810355191503853040357155275733333124624513530099 + +B == +143566909 + +G == 5 +---------------------------------------------------------------- +Certificate of primality for: +187429931674053784626487560729643601208757374994177258429930699354770049369025096447 + +A == +464222094814208047161771036072622485188658077940154689939306386289983787983 + +B == +201875281 + +G == 5 +---------------------------------------------------------------- +Certificate of primality for: +100579220846502621074093727119851331775052664444339632682598589456666938521976625305832917563 + +A == +187429931674053784626487560729643601208757374994177258429930699354770049369025096447 + +B == +268311523 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +1173616081309758475197022137833792133815753368965945885089720153370737965497134878651384030219765163 + +A == +100579220846502621074093727119851331775052664444339632682598589456666938521976625305832917563 + +B == +5834287 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +191456913489905913185935197655672585713573070349044195411728114905691721186574907738081340754373032735283623 + +A == +1173616081309758475197022137833792133815753368965945885089720153370737965497134878651384030219765163 + +B == +81567097 + +G == 5 +---------------------------------------------------------------- +Certificate of primality for: +57856530489201750164178576399448868489243874083056587683743345599898489554401618943240901541005080049321706789987519 + +A == +191456913489905913185935197655672585713573070349044195411728114905691721186574907738081340754373032735283623 + +B == +151095433 + +G == 7 +---------------------------------------------------------------- +Certificate of primality for: +13790529750452576698109671710773784949185621244122040804792403407272729038377767162233653248852099545134831722512085881814803 + +A == +57856530489201750164178576399448868489243874083056587683743345599898489554401618943240901541005080049321706789987519 + +B == +119178679 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +7075985989000817742677547821106534174334812111605018857703825637170140040509067704269696198231266351631132464035671858077052876058979 + +A == +13790529750452576698109671710773784949185621244122040804792403407272729038377767162233653248852099545134831722512085881814803 + +B == +256552363 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +1227273006232588072907488910282307435921226646895131225407452056677899411162892829564455154080310937471747140942360789623819327234258162420463 + +A == +7075985989000817742677547821106534174334812111605018857703825637170140040509067704269696198231266351631132464035671858077052876058979 + +B == +86720989 + +G == 5 +---------------------------------------------------------------- +Certificate of primality for: +446764896913554613686067036908702877942872355053329937790398156069936255759889884246832779737114032666318220500106499161852193765380831330106375235763 + +A == +1227273006232588072907488910282307435921226646895131225407452056677899411162892829564455154080310937471747140942360789623819327234258162420463 + +B == +182015287 + +G == 2 +---------------------------------------------------------------- +Certificate of primality for: +5290203010849586596974953717018896543907195901082056939587768479377028575911127944611236020459652034082251335583308070846379514569838984811187823420951275243 + +A == +446764896913554613686067036908702877942872355053329937790398156069936255759889884246832779737114032666318220500106499161852193765380831330106375235763 + +B == +5920567 + +G == 2 +---------------------------------------------------------------- + + +Took 3454 ticks, 521 bits +P == 5290203010849586596974953717018896543907195901082056939587768479377028575911127944611236020459652034082251335583308070846379514569838984811187823420951275243 +Q == 446764896913554613686067036908702877942872355053329937790398156069936255759889884246832779737114032666318220500106499161852193765380831330106375235763 diff --git a/etc/timer.asm b/etc/timer.asm new file mode 100644 index 0000000..35890d9 --- /dev/null +++ b/etc/timer.asm @@ -0,0 +1,37 @@ +; x86 timer in NASM +; +; Tom St Denis, tomstdenis@iahu.ca +[bits 32] +[section .data] +time dd 0, 0 + +[section .text] + +%ifdef USE_ELF +[global t_start] +t_start: +%else +[global _t_start] +_t_start: +%endif + push edx + push eax + rdtsc + mov [time+0],edx + mov [time+4],eax + pop eax + pop edx + ret + +%ifdef USE_ELF +[global t_read] +t_read: +%else +[global _t_read] +_t_read: +%endif + rdtsc + sub eax,[time+4] + sbb edx,[time+0] + ret + \ No newline at end of file diff --git a/etc/tune.c b/etc/tune.c new file mode 100644 index 0000000..11ce210 --- /dev/null +++ b/etc/tune.c @@ -0,0 +1,176 @@ +/* Tune the Karatsuba parameters + * + * Tom St Denis, tomstdenis@iahu.ca + */ +#include +#include + +/* how many times todo each size mult. Depends on your computer. For slow computers + * this can be low like 5 or 10. For fast [re: Athlon] should be 25 - 50 or so + */ +#define TIMES 50 + + +#ifndef X86_TIMER + +/* generic ISO C timer */ +ulong64 __T; +void t_start(void) { __T = clock(); } +ulong64 t_read(void) { return clock() - __T; } + +#else +extern void t_start(void); +extern ulong64 t_read(void); +#endif + +ulong64 +time_mult (int max) +{ + int x, y; + mp_int a, b, c; + + mp_init (&a); + mp_init (&b); + mp_init (&c); + + t_start(); + for (x = 32; x <= max; x += 4) { + mp_rand (&a, x); + mp_rand (&b, x); + for (y = 0; y < TIMES; y++) { + mp_mul (&a, &b, &c); + } + } + mp_clear (&a); + mp_clear (&b); + mp_clear (&c); + return t_read(); +} + +ulong64 +time_sqr (int max) +{ + int x, y; + mp_int a, b; + + mp_init (&a); + mp_init (&b); + + t_start(); + for (x = 32; x <= max; x += 4) { + mp_rand (&a, x); + for (y = 0; y < TIMES; y++) { + mp_sqr (&a, &b); + } + } + mp_clear (&a); + mp_clear (&b); + return t_read(); +} + +int +main (void) +{ + int best_kmult, best_tmult, best_ksquare, best_tsquare, counter; + ulong64 best, ti; + FILE *log; + + best_kmult = best_ksquare = best_tmult = best_tsquare = 0; + /* tune multiplication first */ + + /* effectively turn TOOM off */ + TOOM_SQR_CUTOFF = TOOM_MUL_CUTOFF = 100000; + + log = fopen ("mult.log", "w"); + best = -1; + counter = 16; + for (KARATSUBA_MUL_CUTOFF = 8; KARATSUBA_MUL_CUTOFF <= 200; KARATSUBA_MUL_CUTOFF++) { + ti = time_mult (300); + printf ("%4d : %9llu \r", KARATSUBA_MUL_CUTOFF, ti); + fprintf (log, "%d, %llu\n", KARATSUBA_MUL_CUTOFF, ti); + fflush (stdout); + if (ti < best) { + printf ("New best: %llu, %d \r", ti, KARATSUBA_MUL_CUTOFF); + best = ti; + best_kmult = KARATSUBA_MUL_CUTOFF; + counter = 16; + } else if (--counter == 0) { + printf("No better found in 16 trials.\n"); + break; + } + } + fclose (log); + printf("Karatsuba Multiplier Cutoff (KARATSUBA_MUL_CUTOFF) == %d\n", best_kmult); + + /* tune squaring */ + log = fopen ("sqr.log", "w"); + best = -1; + counter = 16; + for (KARATSUBA_SQR_CUTOFF = 8; KARATSUBA_SQR_CUTOFF <= 200; KARATSUBA_SQR_CUTOFF++) { + ti = time_sqr (300); + printf ("%4d : %9llu \r", KARATSUBA_SQR_CUTOFF, ti); + fprintf (log, "%d, %llu\n", KARATSUBA_SQR_CUTOFF, ti); + fflush (stdout); + if (ti < best) { + printf ("New best: %llu, %d \r", ti, KARATSUBA_SQR_CUTOFF); + best = ti; + best_ksquare = KARATSUBA_SQR_CUTOFF; + counter = 16; + } else if (--counter == 0) { + printf("No better found in 16 trials.\n"); + break; + } + } + fclose (log); + printf("Karatsuba Squaring Cutoff (KARATSUBA_SQR_CUTOFF) == %d\n", best_ksquare); + + KARATSUBA_MUL_CUTOFF = best_kmult; + KARATSUBA_SQR_CUTOFF = best_ksquare; + + /* tune TOOM mult */ + counter = 16; + log = fopen ("tmult.log", "w"); + best = -1; + for (TOOM_MUL_CUTOFF = best_kmult*5; TOOM_MUL_CUTOFF <= 800; TOOM_MUL_CUTOFF++) { + ti = time_mult (1200); + printf ("%4d : %9llu \r", TOOM_MUL_CUTOFF, ti); + fprintf (log, "%d, %llu\n", TOOM_MUL_CUTOFF, ti); + fflush (stdout); + if (ti < best) { + printf ("New best: %llu, %d \r", ti, TOOM_MUL_CUTOFF); + best = ti; + best_tmult = TOOM_MUL_CUTOFF; + counter = 16; + } else if (--counter == 0) { + printf("No better found in 16 trials.\n"); + break; + } + } + fclose (log); + printf("Toom-Cook Multiplier Cutoff (TOOM_MUL_CUTOFF) == %d\n", best_tmult); + + /* tune TOOM sqr */ + log = fopen ("tsqr.log", "w"); + best = -1; + counter = 16; + for (TOOM_SQR_CUTOFF = best_ksquare*3; TOOM_SQR_CUTOFF <= 800; TOOM_SQR_CUTOFF++) { + ti = time_sqr (1200); + printf ("%4d : %9llu \r", TOOM_SQR_CUTOFF, ti); + fprintf (log, "%d, %llu\n", TOOM_SQR_CUTOFF, ti); + fflush (stdout); + if (ti < best) { + printf ("New best: %llu, %d \r", ti, TOOM_SQR_CUTOFF); + best = ti; + best_tsquare = TOOM_SQR_CUTOFF; + counter = 16; + } else if (--counter == 0) { + printf("No better found in 16 trials.\n"); + break; + } + } + fclose (log); + printf("Toom-Cook Squaring Cutoff (TOOM_SQR_CUTOFF) == %d\n", best_tsquare); + + + return 0; +} diff --git a/gen.pl b/gen.pl new file mode 100644 index 0000000..7236591 --- /dev/null +++ b/gen.pl @@ -0,0 +1,17 @@ +#!/usr/bin/perl -w +# +# Generates a "single file" you can use to quickly +# add the whole source without any makefile troubles +# +use strict; + +open( OUT, ">mpi.c" ) or die "Couldn't open mpi.c for writing: $!"; +foreach my $filename (glob "bn*.c") { + open( SRC, "<$filename" ) or die "Couldn't open $filename for reading: $!"; + print OUT "/* Start: $filename */\n"; + print OUT while ; + print OUT "\n/* End: $filename */\n\n"; + close SRC or die "Error closing $filename after reading: $!"; +} +print OUT "\n/* EOF */\n"; +close OUT or die "Error closing mpi.c after writing: $!"; \ No newline at end of file diff --git a/logs/README b/logs/README new file mode 100644 index 0000000..ea20c81 --- /dev/null +++ b/logs/README @@ -0,0 +1,13 @@ +To use the pretty graphs you have to first build/run the ltmtest from the root directory of the package. +Todo this type + +make timing ; ltmtest + +in the root. It will run for a while [about ten minutes on most PCs] and produce a series of .log files in logs/. + +After doing that run "gnuplot graphs.dem" to make the PNGs. If you managed todo that all so far just open index.html to view +them all :-) + +Have fun + +Tom \ No newline at end of file diff --git a/logs/add.log b/logs/add.log new file mode 100644 index 0000000..e53b415 --- /dev/null +++ b/logs/add.log @@ -0,0 +1,16 @@ +224 20297071 +448 15151383 +672 13088682 +896 11111587 +1120 9240621 +1344 8221878 +1568 7227434 +1792 6718051 +2016 6042524 +2240 5685200 +2464 5240465 +2688 4818032 +2912 4412794 +3136 4155883 +3360 3927078 +3584 3722138 diff --git a/logs/addsub.png b/logs/addsub.png new file mode 100644 index 0000000000000000000000000000000000000000..e733f8d7622be239c70d1708c6ef8c79ec12576d GIT binary patch literal 7084 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�b6R|d)pk7cpqszx>)5@= zG*YVkSPnP*9F&wQRzNleIV%K1iQCTO@mKdf)sdp{{b{a{%^9VNi}=#b2fnEyw#_hw z=eXqOi!P|tK_x?DOf|WR#zL#D4%llVof-Nzbhw~dI4ngoopoNmoS&11$9tAn3#(qb zpsQ8=57>&lARATbWzK5ux>&}IB6A;G(PqeIo5iBMAjpPo5m4w0*@QXCc?GbxQg~)2 zHX>)pX44H-u?m*4t}(vn0`8{bTRm!n-o9e!W2TDisP$@JDMkBfIx*Kkq;Drhd*H0N zJmA(0g{OF)l}X|KViNg|iDjrbUG~nN+B##0`duh}8YmAZABbU@Q+Qf@)(Q&mkmkj> z+H*_c0-!+j@)s`Lh`?u3!v#VObc#jk<#fc7#+fsN-G~&-8gQj(ncpV#nrlwe3sZ1J$Vk$^ z*s=wMU-VRa{2Zu;(E3Jq*!w)C!&M1t25U#I;G7&5jA7nt3fAa*YoF~3ut&bTmL`TR z3#|opIFwqg!9<2yTEvktm*a4J!>6`qqh%7=C!Y2wnmHm!8a7Zz@}pG@r=@89omS(a z5P8v!x!@#gHFYF-@2~I5oYGvuX@rZpVN#E|0YrAXkyJMte$EMHW*7#DR=Nu1VaKNA z9`KyYtKLfoyUMztWKnzT(PjLYXhqtpq#d3!Pt+1UJt8HK_i3zLlB_>^G|AR;=4A5m zw7Yl7g0m)mjb#cM{u>OzeLBE#rPHKr!&r9TgBEi3>jw;4=V{+Pv8;l75&IIRxtjE| z!D@3LJ*71~GU z@AfUT(oDsz#Ep?rTXZmN$&D6s1t&CJ`x@6CO?SA)@~2RXe=!Ci^6H`C9){pl zPcz^kc>MqWe6R_FC;I;!2markuP$GUa7T>4#;yt*&G0^(+?*{rTYT)8x18R?=8lUN zpNCZ9=G^1M*hkruv%X>8i}W7;JwF_IxjLetUqKa*yHvta~&ddyjuA<>Ce{eSL0moGa6VF~9uQ^$ga8!l*{oLOwuU6T8 zk4ks#jK&vV>4v`=Cm1U$u6are%@At$xDCHK`(|E5vBa%gPcopGP?#H(W}A8?_R08N!^*^r z5m&~bdSdIMLKzk|yHs<+vLSNq*F=fI(N`Uxu$odc-9K#C$_syus!3c6x%-WNit6}y zaW3+oyLwMgx5>n%PD%|QegiG)`=YNm%82v&0$e<5;pv%Ab)`Pq->UNOKTAj?=~B~G zGfnQ5%LccFO*m=`ZC;0##`^f8D+jA?Y&t%6nu}c;2w&CTsH{UC8Q&#EJJCNJIXzjj z-_rZ~p{SwAkF&Y{n^cNjcYCc2OWL%Zo$lRkvA0v~8@VyJVVBjNp64N3jlw&7?J?}q zy}Ga89u@SZ)cyI>L557f3*X=ubX>wV6tfH_Op$7^wrh8 fCEx~3>K%{D{m1lN$?NnaB3LIo7u(#;=&%0+DMbmE literal 0 HcmV?d00001 diff --git a/logs/expt.log b/logs/expt.log new file mode 100644 index 0000000..2597b48 --- /dev/null +++ b/logs/expt.log @@ -0,0 +1,7 @@ +513 745 +769 282 +1025 130 +2049 20 +2561 11 +3073 6 +4097 2 diff --git a/logs/expt.png b/logs/expt.png new file mode 100644 index 0000000000000000000000000000000000000000..59bafa2a8b2fc8e1298799eccc4b65defb299530 GIT binary patch literal 7439 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 YHd{KM{eJE4F)VScx7k^gZ-tNjAMn|BO#lD@ literal 0 HcmV?d00001 diff --git a/logs/expt_2k.log b/logs/expt_2k.log new file mode 100644 index 0000000..f4c282c --- /dev/null +++ b/logs/expt_2k.log @@ -0,0 +1,6 @@ +521 783 +607 585 +1279 138 +2203 39 +3217 15 +4253 6 diff --git a/logs/expt_dr.log b/logs/expt_dr.log new file mode 100644 index 0000000..c552e12 --- /dev/null +++ b/logs/expt_dr.log @@ -0,0 +1,7 @@ +532 1296 +784 551 +1036 283 +1540 109 +2072 52 +3080 18 +4116 7 diff --git a/logs/graphs.dem b/logs/graphs.dem new file mode 100644 index 0000000..d5c9b8a --- /dev/null +++ b/logs/graphs.dem @@ -0,0 +1,17 @@ +set terminal png +set size 1.75 +set ylabel "Operations per Second" +set xlabel "Operand size (bits)" + +set output "addsub.png" +plot 'add.log' smooth bezier title "Addition", 'sub.log' smooth bezier title "Subtraction" + +set output "mult.png" +plot 'sqr.log' smooth bezier title "Squaring (without Karatsuba)", 'sqr_kara.log' smooth bezier title "Squaring (Karatsuba)", 'mult.log' smooth bezier title "Multiplication (without Karatsuba)", 'mult_kara.log' smooth bezier title "Multiplication (Karatsuba)" + +set output "expt.png" +plot 'expt.log' smooth bezier title "Exptmod (Montgomery)", 'expt_dr.log' smooth bezier title "Exptmod (Dimminished Radix)", 'expt_2k.log' smooth bezier title "Exptmod (2k Reduction)" + +set output "invmod.png" +plot 'invmod.log' smooth bezier title "Modular Inverse" + diff --git a/logs/index.html b/logs/index.html new file mode 100644 index 0000000..19fe403 --- /dev/null +++ b/logs/index.html @@ -0,0 +1,24 @@ + + +LibTomMath Log Plots + + + +

Addition and Subtraction

+
+
+ +

Multipliers

+
+
+ +

Exptmod

+
+
+ +

Modular Inverse

+
+
+ + + \ No newline at end of file diff --git a/logs/invmod.log b/logs/invmod.log new file mode 100644 index 0000000..c9294ef --- /dev/null +++ b/logs/invmod.log @@ -0,0 +1,32 @@ +112 17364 +224 8643 +336 8867 +448 6228 +560 4737 +672 2259 +784 2899 +896 1497 +1008 1238 +1120 1010 +1232 870 +1344 1265 +1456 1102 +1568 981 +1680 539 +1792 484 +1904 722 +2016 392 +2128 604 +2240 551 +2352 511 +2464 469 +2576 263 +2688 247 +2800 227 +2912 354 +3024 336 +3136 312 +3248 296 +3360 166 +3472 155 +3584 248 diff --git a/logs/invmod.png b/logs/invmod.png new file mode 100644 index 0000000000000000000000000000000000000000..baa287fe2085f953be78c2839236895241048624 GIT binary patch literal 5735 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 literal 0 HcmV?d00001 diff --git a/logs/k7/expt.log b/logs/k7/expt.log new file mode 100644 index 0000000..46bb50b --- /dev/null +++ b/logs/k7/expt.log @@ -0,0 +1,7 @@ +513 664 +769 256 +1025 117 +2049 17 +2561 9 +3073 5 +4097 2 diff --git a/logs/k7/expt.png b/logs/k7/expt.png new file mode 100644 index 0000000000000000000000000000000000000000..fc82677a7f6c107c5d587ce659b9fb4e32ac3035 GIT binary patch literal 6490 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 WFAN^whI(!=G)vPhCO0>sQU3>q@ODK2 literal 0 HcmV?d00001 diff --git a/logs/k7/expt_dr.log b/logs/k7/expt_dr.log new file mode 100644 index 0000000..7df658f --- /dev/null +++ b/logs/k7/expt_dr.log @@ -0,0 +1,7 @@ +532 1088 +784 460 +1036 240 +1540 92 +2072 43 +3080 15 +4116 6 diff --git a/logs/k7/graphs.dem b/logs/k7/graphs.dem new file mode 100644 index 0000000..c580495 --- /dev/null +++ b/logs/k7/graphs.dem @@ -0,0 +1,17 @@ +set terminal png color +set size 1.75 +set ylabel "Operations per Second" +set xlabel "Operand size (bits)" + +set output "addsub.png" +plot 'add.log' smooth bezier title "Addition", 'sub.log' smooth bezier title "Subtraction" + +set output "mult.png" +plot 'sqr.log' smooth bezier title "Squaring (without Karatsuba)", 'sqr_kara.log' smooth bezier title "Squaring (Karatsuba)", 'mult.log' smooth bezier title "Multiplication (without Karatsuba)", 'mult_kara.log' smooth bezier title "Multiplication (Karatsuba)" + +set output "expt.png" +plot 'expt.log' smooth bezier title "Exptmod (Montgomery)", 'expt_dr.log' smooth bezier title "Exptmod (Dimminished Radix)" + +set output "invmod.png" +plot 'invmod.log' smooth bezier title "Modular Inverse" + diff --git a/logs/k7/index.html b/logs/k7/index.html new file mode 100644 index 0000000..19fe403 --- /dev/null +++ b/logs/k7/index.html @@ -0,0 +1,24 @@ + + +LibTomMath Log Plots + + + +

Addition and Subtraction

+
+
+ +

Multipliers

+
+
+ +

Exptmod

+
+
+ +

Modular Inverse

+
+
+ + + \ No newline at end of file diff --git a/logs/k7/invmod.log b/logs/k7/invmod.log new file mode 100644 index 0000000..d1198fb --- /dev/null +++ b/logs/k7/invmod.log @@ -0,0 +1,32 @@ +112 16248 +224 8192 +336 5320 +448 3560 +560 2728 +672 2064 +784 1704 +896 2176 +1008 1184 +1120 976 +1232 1280 +1344 1176 +1456 624 +1568 912 +1680 504 +1792 452 +1904 658 +2016 608 +2128 336 +2240 312 +2352 288 +2464 264 +2576 408 +2688 376 +2800 354 +2912 198 +3024 307 +3136 173 +3248 162 +3360 256 +3472 145 +3584 226 diff --git a/logs/k7/invmod.png b/logs/k7/invmod.png new file mode 100644 index 0000000000000000000000000000000000000000..a497a723f46328692f503d2732afb3c524e1af82 GIT binary patch literal 5730 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 F^B;aW0Y(4- literal 0 HcmV?d00001 diff --git a/logs/k7/mult.log b/logs/k7/mult.log new file mode 100644 index 0000000..4b1bff3 --- /dev/null +++ b/logs/k7/mult.log @@ -0,0 +1,17 @@ +896 322904 +1344 151592 +1792 90472 +2240 59984 +2688 42624 +3136 31872 +3584 24704 +4032 19704 +4480 16096 +4928 13376 +5376 11272 +5824 9616 +6272 8360 +6720 7304 +7168 1664 +7616 1472 +8064 1328 diff --git a/logs/k7/mult.png b/logs/k7/mult.png new file mode 100644 index 0000000000000000000000000000000000000000..3cd8a9349df44a189a69b8dc401e9140c21b28e5 GIT binary patch literal 8263 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 literal 0 HcmV?d00001 diff --git a/logs/mult_kara.log b/logs/mult_kara.log new file mode 100644 index 0000000..6edc439 --- /dev/null +++ b/logs/mult_kara.log @@ -0,0 +1,33 @@ +924 374171 +1147 243163 +1371 177111 +1596 134465 +1819 105619 +2044 85145 +2266 70086 +2488 58717 +2715 49869 +2939 42894 +3164 37389 +3387 33510 +3610 29993 +3836 27205 +4060 24751 +4281 22576 +4508 20670 +4732 19019 +4954 17527 +5180 16217 +5404 15044 +5624 14003 +5849 13051 +6076 12067 +6300 11438 +6524 10772 +6748 10298 +6972 9715 +7195 9330 +7416 8836 +7644 8465 +7864 8042 +8091 7735 diff --git a/logs/p4/README b/logs/p4/README new file mode 100644 index 0000000..ea20c81 --- /dev/null +++ b/logs/p4/README @@ -0,0 +1,13 @@ +To use the pretty graphs you have to first build/run the ltmtest from the root directory of the package. +Todo this type + +make timing ; ltmtest + +in the root. It will run for a while [about ten minutes on most PCs] and produce a series of .log files in logs/. + +After doing that run "gnuplot graphs.dem" to make the PNGs. If you managed todo that all so far just open index.html to view +them all :-) + +Have fun + +Tom \ No newline at end of file diff --git a/logs/p4/add.log b/logs/p4/add.log new file mode 100644 index 0000000..72b2506 --- /dev/null +++ b/logs/p4/add.log @@ -0,0 +1,16 @@ +224 8113248 +448 6585584 +672 5687678 +896 4761144 +1120 4111592 +1344 3995154 +1568 3532387 +1792 3225400 +2016 2963960 +2240 2720112 +2464 2533952 +2688 2307168 +2912 2287064 +3136 2150160 +3360 2035992 +3584 1936304 diff --git a/logs/p4/addsub.png b/logs/p4/addsub.png new file mode 100644 index 0000000000000000000000000000000000000000..f4398cad4639e3747210de5308613b67f243ff28 GIT binary patch literal 6898 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 H_{@I*pd^d1 literal 0 HcmV?d00001 diff --git a/logs/p4/expt.log b/logs/p4/expt.log new file mode 100644 index 0000000..3e6ffb8 --- /dev/null +++ b/logs/p4/expt.log @@ -0,0 +1,7 @@ +513 195 +769 68 +1025 31 +2049 4 +2561 2 +3073 1 +4097 0 diff --git a/logs/p4/expt.png b/logs/p4/expt.png new file mode 100644 index 0000000000000000000000000000000000000000..dac1ce2cbdbbbd56ff8213d3e4616e022b186988 GIT binary patch literal 6587 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 literal 0 HcmV?d00001 diff --git a/logs/p4/expt_dr.log b/logs/p4/expt_dr.log new file mode 100644 index 0000000..2f5f6a3 --- /dev/null +++ b/logs/p4/expt_dr.log @@ -0,0 +1,7 @@ +532 393 +784 158 +1036 79 +1540 27 +2072 12 +3080 4 +4116 1 diff --git a/logs/p4/graphs.dem b/logs/p4/graphs.dem new file mode 100644 index 0000000..c580495 --- /dev/null +++ b/logs/p4/graphs.dem @@ -0,0 +1,17 @@ +set terminal png color +set size 1.75 +set ylabel "Operations per Second" +set xlabel "Operand size (bits)" + +set output "addsub.png" +plot 'add.log' smooth bezier title "Addition", 'sub.log' smooth bezier title "Subtraction" + +set output "mult.png" +plot 'sqr.log' smooth bezier title "Squaring (without Karatsuba)", 'sqr_kara.log' smooth bezier title "Squaring (Karatsuba)", 'mult.log' smooth bezier title "Multiplication (without Karatsuba)", 'mult_kara.log' smooth bezier title "Multiplication (Karatsuba)" + +set output "expt.png" +plot 'expt.log' smooth bezier title "Exptmod (Montgomery)", 'expt_dr.log' smooth bezier title "Exptmod (Dimminished Radix)" + +set output "invmod.png" +plot 'invmod.log' smooth bezier title "Modular Inverse" + diff --git a/logs/p4/index.html b/logs/p4/index.html new file mode 100644 index 0000000..19fe403 --- /dev/null +++ b/logs/p4/index.html @@ -0,0 +1,24 @@ + + +LibTomMath Log Plots + + + +

Addition and Subtraction

+
+
+ +

Multipliers

+
+
+ +

Exptmod

+
+
+ +

Modular Inverse

+
+
+ + + \ No newline at end of file diff --git a/logs/p4/invmod.log b/logs/p4/invmod.log new file mode 100644 index 0000000..096087b --- /dev/null +++ b/logs/p4/invmod.log @@ -0,0 +1,32 @@ +112 13608 +224 6872 +336 4264 +448 2792 +560 2144 +672 1560 +784 1296 +896 1672 +1008 896 +1120 736 +1232 1024 +1344 888 +1456 472 +1568 680 +1680 373 +1792 328 +1904 484 +2016 436 +2128 232 +2240 211 +2352 200 +2464 177 +2576 293 +2688 262 +2800 251 +2912 137 +3024 216 +3136 117 +3248 113 +3360 181 +3472 98 +3584 158 diff --git a/logs/p4/invmod.png b/logs/p4/invmod.png new file mode 100644 index 0000000000000000000000000000000000000000..3b0580fe05b79a283f0880b807edfeadb8df35a3 GIT binary patch literal 5596 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 GcmDwqPR=U; literal 0 HcmV?d00001 diff --git a/logs/p4/mult.log b/logs/p4/mult.log new file mode 100644 index 0000000..6e43806 --- /dev/null +++ b/logs/p4/mult.log @@ -0,0 +1,17 @@ +896 77600 +1344 35776 +1792 19688 +2240 13248 +2688 9424 +3136 7056 +3584 5464 +4032 4368 +4480 3568 +4928 2976 +5376 2520 +5824 2152 +6272 1872 +6720 1632 +7168 650 +7616 576 +8064 515 diff --git a/logs/p4/mult.png b/logs/p4/mult.png new file mode 100644 index 0000000000000000000000000000000000000000..8623558f5c4dc9bf4a529f8a7f7c652e9cb7d0a9 GIT binary patch literal 8100 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 UH=xNj;+S8SrZ(I2x51JB2bWI#Z2$lO literal 0 HcmV?d00001 diff --git a/logs/p4/mult_kara.log b/logs/p4/mult_kara.log new file mode 100644 index 0000000..e1d50a6 --- /dev/null +++ b/logs/p4/mult_kara.log @@ -0,0 +1,17 @@ +896 77752 +1344 35832 +1792 19688 +2240 14704 +2688 10832 +3136 8336 +3584 6600 +4032 5424 +4480 4648 +4928 3976 +5376 3448 +5824 3016 +6272 2664 +6720 2384 +7168 2120 +7616 1912 +8064 1752 diff --git a/logs/p4/sqr.log b/logs/p4/sqr.log new file mode 100644 index 0000000..b133fb3 --- /dev/null +++ b/logs/p4/sqr.log @@ -0,0 +1,17 @@ +896 128088 +1344 63640 +1792 37968 +2240 25488 +2688 18176 +3136 13672 +3584 4920 +4032 3912 +4480 3160 +4928 2616 +5376 2216 +5824 1896 +6272 1624 +6720 1408 +7168 1240 +7616 1096 +8064 984 diff --git a/logs/p4/sqr_kara.log b/logs/p4/sqr_kara.log new file mode 100644 index 0000000..13e4f3e --- /dev/null +++ b/logs/p4/sqr_kara.log @@ -0,0 +1,17 @@ +896 127456 +1344 63752 +1792 37920 +2240 25440 +2688 18200 +3136 13728 +3584 10968 +4032 9072 +4480 7608 +4928 6440 +5376 5528 +5824 4768 +6272 4328 +6720 3888 +7168 3504 +7616 3176 +8064 2896 diff --git a/logs/p4/sub.log b/logs/p4/sub.log new file mode 100644 index 0000000..424de32 --- /dev/null +++ b/logs/p4/sub.log @@ -0,0 +1,16 @@ +224 7355896 +448 6162880 +672 5218984 +896 4622776 +1120 3999320 +1344 3629480 +1568 3290384 +1792 2954752 +2016 2737056 +2240 2563320 +2464 2451928 +2688 2310920 +2912 2139048 +3136 2034080 +3360 1890800 +3584 1808624 diff --git a/logs/sqr.log b/logs/sqr.log new file mode 100644 index 0000000..81fa612 --- /dev/null +++ b/logs/sqr.log @@ -0,0 +1,33 @@ +922 471095 +1147 337137 +1366 254327 +1596 199732 +1819 161225 +2044 132852 +2268 111493 +2490 94864 +2715 81745 +2940 71187 +3162 62575 +3387 55418 +3612 14540 +3836 12944 +4060 11627 +4281 10546 +4508 9502 +4730 8688 +4954 7937 +5180 7273 +5402 6701 +5627 6189 +5850 5733 +6076 5310 +6300 4933 +6522 4631 +6748 4313 +6971 4064 +7196 3801 +7420 3576 +7642 3388 +7868 3191 +8092 3020 diff --git a/logs/sqr.old b/logs/sqr.old new file mode 100644 index 0000000..3c85882 --- /dev/null +++ b/logs/sqr.old @@ -0,0 +1,17 @@ +896 382617 +1344 207161 +1792 131522 +2240 90775 +2688 66652 +3136 50955 +3584 11678 +4032 9342 +4480 7684 +4928 6382 +5376 5399 +5824 4545 +6272 3994 +6720 3490 +7168 3075 +7616 2733 +8064 2428 diff --git a/logs/sqr_kara.log b/logs/sqr_kara.log new file mode 100644 index 0000000..3b547cf --- /dev/null +++ b/logs/sqr_kara.log @@ -0,0 +1,33 @@ +922 470930 +1148 337217 +1372 254433 +1596 199827 +1820 161204 +2043 132871 +2267 111522 +2488 94932 +2714 81814 +2939 71231 +3164 62616 +3385 55467 +3611 44426 +3836 40695 +4060 37391 +4283 34371 +4508 31779 +4732 29499 +4956 27426 +5177 25598 +5403 23944 +5628 22416 +5851 21052 +6076 19781 +6299 18588 +6523 17539 +6746 16618 +6972 15705 +7196 13582 +7420 13004 +7643 12496 +7868 11963 +8092 11497 diff --git a/logs/sub.log b/logs/sub.log new file mode 100644 index 0000000..f1ade94 --- /dev/null +++ b/logs/sub.log @@ -0,0 +1,16 @@ +224 16370431 +448 13327848 +672 11009401 +896 9125342 +1120 7930419 +1344 7114040 +1568 6506998 +1792 5899346 +2016 5435327 +2240 5038931 +2464 4696364 +2688 4425678 +2912 4134476 +3136 3913280 +3360 3692536 +3584 3505219 diff --git a/makefile b/makefile new file mode 100644 index 0000000..07b7842 --- /dev/null +++ b/makefile @@ -0,0 +1,121 @@ +#Makefile for GCC +# +#Tom St Denis +CFLAGS += -I./ -Wall -W -Wshadow -Wsign-compare + +#for speed +CFLAGS += -O3 -funroll-loops + +#for size +#CFLAGS += -Os + +#x86 optimizations [should be valid for any GCC install though] +CFLAGS += -fomit-frame-pointer + +VERSION=0.30 + +default: libtommath.a + +#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_mp_xor.o bn_mp_and.o bn_mp_or.o bn_mp_rand.o bn_mp_montgomery_calc_normalization.o \ +bn_mp_prime_is_divisible.o bn_prime_tab.o bn_mp_prime_fermat.o bn_mp_prime_miller_rabin.o \ +bn_mp_prime_is_prime.o bn_mp_prime_next_prime.o bn_mp_dr_reduce.o \ +bn_mp_dr_is_modulus.o bn_mp_dr_setup.o bn_mp_reduce_setup.o \ +bn_mp_toom_mul.o bn_mp_toom_sqr.o bn_mp_div_3.o bn_s_mp_exptmod.o \ +bn_mp_reduce_2k.o bn_mp_reduce_is_2k.o bn_mp_reduce_2k_setup.o \ +bn_mp_radix_smap.o bn_mp_read_radix.o bn_mp_toradix.o bn_mp_radix_size.o \ +bn_mp_fread.o bn_mp_fwrite.o bn_mp_cnt_lsb.o bn_error.o \ +bn_mp_init_multi.o bn_mp_clear_multi.o bn_prime_sizes_tab.o bn_mp_exteuclid.o bn_mp_toradix_n.o \ +bn_mp_prime_random_ex.o bn_mp_get_int.o bn_mp_sqrt.o bn_mp_is_square.o bn_mp_init_set.o \ +bn_mp_init_set_int.o + +libtommath.a: $(OBJECTS) + $(AR) $(ARFLAGS) libtommath.a $(OBJECTS) + ranlib libtommath.a + +install: libtommath.a + install -d -g root -o root $(DESTDIR)$(LIBPATH) + install -d -g root -o root $(DESTDIR)$(INCPATH) + install -g root -o root $(LIBNAME) $(DESTDIR)$(LIBPATH) + install -g root -o root $(HEADERS) $(DESTDIR)$(INCPATH) + +test: libtommath.a demo/demo.o + $(CC) demo/demo.o libtommath.a -o test + +mtest: test + cd mtest ; $(CC) $(CFLAGS) mtest.c -o mtest -s + +timing: libtommath.a + $(CC) $(CFLAGS) -DTIMER demo/demo.c libtommath.a -o ltmtest -s + +# makes the LTM book DVI file, requires tetex, perl and makeindex [part of tetex I think] +docdvi: tommath.src + cd pics ; make + echo "hello" > tommath.ind + perl booker.pl + latex tommath > /dev/null + latex tommath > /dev/null + makeindex tommath + latex tommath > /dev/null + +# poster, makes the single page PDF poster +poster: poster.tex + pdflatex poster + rm -f poster.aux poster.log + +# makes the LTM book PDF file, requires tetex, cleans up the LaTeX temp files +docs: docdvi + dvipdf tommath + rm -f tommath.log tommath.aux tommath.dvi tommath.idx tommath.toc tommath.lof tommath.ind tommath.ilg + cd pics ; make clean + +#LTM user manual +mandvi: bn.tex + echo "hello" > bn.ind + latex bn > /dev/null + latex bn > /dev/null + makeindex bn + latex bn > /dev/null + +#LTM user manual [pdf] +manual: mandvi + pdflatex bn >/dev/null + rm -f bn.aux bn.dvi bn.log bn.idx bn.lof bn.out bn.toc + +clean: + rm -f *.bat *.pdf *.o *.a *.obj *.lib *.exe *.dll etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest mtest/mtest.exe \ + *.idx *.toc *.log *.aux *.dvi *.lof *.ind *.ilg *.ps *.log *.s mpi.c + cd etc ; make clean + cd pics ; make clean + +zipup: clean manual poster docs + perl gen.pl ; mv mpi.c pre_gen/ ; \ + cd .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \ + cp -R ./libtommath/* ./libtommath-$(VERSION)/ ; \ + tar -c libtommath-$(VERSION)/* | bzip2 -9vvc > ltm-$(VERSION).tar.bz2 ; \ + zip -9 -r ltm-$(VERSION).zip libtommath-$(VERSION)/* diff --git a/makefile.bcc b/makefile.bcc new file mode 100644 index 0000000..6874d2f --- /dev/null +++ b/makefile.bcc @@ -0,0 +1,41 @@ +# +# Borland C++Builder Makefile (makefile.bcc) +# + + +LIB = tlib +CC = bcc32 +CFLAGS = -c -O2 -I. + +OBJECTS=bncore.obj bn_mp_init.obj bn_mp_clear.obj bn_mp_exch.obj bn_mp_grow.obj bn_mp_shrink.obj \ +bn_mp_clamp.obj bn_mp_zero.obj bn_mp_set.obj bn_mp_set_int.obj bn_mp_init_size.obj bn_mp_copy.obj \ +bn_mp_init_copy.obj bn_mp_abs.obj bn_mp_neg.obj bn_mp_cmp_mag.obj bn_mp_cmp.obj bn_mp_cmp_d.obj \ +bn_mp_rshd.obj bn_mp_lshd.obj bn_mp_mod_2d.obj bn_mp_div_2d.obj bn_mp_mul_2d.obj bn_mp_div_2.obj \ +bn_mp_mul_2.obj bn_s_mp_add.obj bn_s_mp_sub.obj bn_fast_s_mp_mul_digs.obj bn_s_mp_mul_digs.obj \ +bn_fast_s_mp_mul_high_digs.obj bn_s_mp_mul_high_digs.obj bn_fast_s_mp_sqr.obj bn_s_mp_sqr.obj \ +bn_mp_add.obj bn_mp_sub.obj bn_mp_karatsuba_mul.obj bn_mp_mul.obj bn_mp_karatsuba_sqr.obj \ +bn_mp_sqr.obj bn_mp_div.obj bn_mp_mod.obj bn_mp_add_d.obj bn_mp_sub_d.obj bn_mp_mul_d.obj \ +bn_mp_div_d.obj bn_mp_mod_d.obj bn_mp_expt_d.obj bn_mp_addmod.obj bn_mp_submod.obj \ +bn_mp_mulmod.obj bn_mp_sqrmod.obj bn_mp_gcd.obj bn_mp_lcm.obj bn_fast_mp_invmod.obj bn_mp_invmod.obj \ +bn_mp_reduce.obj bn_mp_montgomery_setup.obj bn_fast_mp_montgomery_reduce.obj bn_mp_montgomery_reduce.obj \ +bn_mp_exptmod_fast.obj bn_mp_exptmod.obj bn_mp_2expt.obj bn_mp_n_root.obj bn_mp_jacobi.obj bn_reverse.obj \ +bn_mp_count_bits.obj bn_mp_read_unsigned_bin.obj bn_mp_read_signed_bin.obj bn_mp_to_unsigned_bin.obj \ +bn_mp_to_signed_bin.obj bn_mp_unsigned_bin_size.obj bn_mp_signed_bin_size.obj \ +bn_mp_xor.obj bn_mp_and.obj bn_mp_or.obj bn_mp_rand.obj bn_mp_montgomery_calc_normalization.obj \ +bn_mp_prime_is_divisible.obj bn_prime_tab.obj bn_mp_prime_fermat.obj bn_mp_prime_miller_rabin.obj \ +bn_mp_prime_is_prime.obj bn_mp_prime_next_prime.obj bn_mp_dr_reduce.obj \ +bn_mp_dr_is_modulus.obj bn_mp_dr_setup.obj bn_mp_reduce_setup.obj \ +bn_mp_toom_mul.obj bn_mp_toom_sqr.obj bn_mp_div_3.obj bn_s_mp_exptmod.obj \ +bn_mp_reduce_2k.obj bn_mp_reduce_is_2k.obj bn_mp_reduce_2k_setup.obj \ +bn_mp_radix_smap.obj bn_mp_read_radix.obj bn_mp_toradix.obj bn_mp_radix_size.obj \ +bn_mp_fread.obj bn_mp_fwrite.obj bn_mp_cnt_lsb.obj bn_error.obj \ +bn_mp_init_multi.obj bn_mp_clear_multi.obj bn_prime_sizes_tab.obj bn_mp_exteuclid.obj bn_mp_toradix_n.obj \ +bn_mp_prime_random_ex.obj bn_mp_get_int.obj bn_mp_sqrt.obj bn_mp_is_square.obj + +TARGET = libtommath.lib + +$(TARGET): $(OBJECTS) + +.c.objbjbjbj: + $(CC) $(CFLAGS) $< + $(LIB) $(TARGET) -+$@ diff --git a/makefile.cygwin_dll b/makefile.cygwin_dll new file mode 100644 index 0000000..e5ab814 --- /dev/null +++ b/makefile.cygwin_dll @@ -0,0 +1,48 @@ +#Makefile for Cygwin-GCC +# +#This makefile will build a Windows DLL [doesn't require cygwin to run] in the file +#libtommath.dll. The import library is in libtommath.dll.a. Remember to add +#"-Wl,--enable-auto-import" to your client build to avoid the auto-import warnings +# +#Tom St Denis +CFLAGS += -I./ -Wall -W -Wshadow -O3 -funroll-loops -mno-cygwin + +#x86 optimizations [should be valid for any GCC install though] +CFLAGS += -fomit-frame-pointer + +default: windll + +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_mp_xor.o bn_mp_and.o bn_mp_or.o bn_mp_rand.o bn_mp_montgomery_calc_normalization.o \ +bn_mp_prime_is_divisible.o bn_prime_tab.o bn_mp_prime_fermat.o bn_mp_prime_miller_rabin.o \ +bn_mp_prime_is_prime.o bn_mp_prime_next_prime.o bn_mp_dr_reduce.o \ +bn_mp_dr_is_modulus.o bn_mp_dr_setup.o bn_mp_reduce_setup.o \ +bn_mp_toom_mul.o bn_mp_toom_sqr.o bn_mp_div_3.o bn_s_mp_exptmod.o \ +bn_mp_reduce_2k.o bn_mp_reduce_is_2k.o bn_mp_reduce_2k_setup.o \ +bn_mp_radix_smap.o bn_mp_read_radix.o bn_mp_toradix.o bn_mp_radix_size.o \ +bn_mp_fread.o bn_mp_fwrite.o bn_mp_cnt_lsb.o bn_error.o \ +bn_mp_init_multi.o bn_mp_clear_multi.o bn_prime_sizes_tab.o bn_mp_exteuclid.o bn_mp_toradix_n.o \ +bn_mp_prime_random_ex.o bn_mp_get_int.o bn_mp_sqrt.o bn_mp_is_square.o + +# make a Windows DLL via Cygwin +windll: $(OBJECTS) + gcc -mno-cygwin -mdll -o libtommath.dll -Wl,--out-implib=libtommath.dll.a -Wl,--export-all-symbols *.o + ranlib libtommath.dll.a + +# build the test program using the windows DLL +test: $(OBJECTS) windll + gcc $(CFLAGS) demo/demo.c libtommath.dll.a -Wl,--enable-auto-import -o test -s + cd mtest ; $(CC) -O3 -fomit-frame-pointer -funroll-loops mtest.c -o mtest -s diff --git a/makefile.msvc b/makefile.msvc new file mode 100644 index 0000000..beeb77e --- /dev/null +++ b/makefile.msvc @@ -0,0 +1,35 @@ +#MSVC Makefile +# +#Tom St Denis + +CFLAGS = /I. /Ox /DWIN32 /W4 + +default: library + +OBJECTS=bncore.obj bn_mp_init.obj bn_mp_clear.obj bn_mp_exch.obj bn_mp_grow.obj bn_mp_shrink.obj \ +bn_mp_clamp.obj bn_mp_zero.obj bn_mp_set.obj bn_mp_set_int.obj bn_mp_init_size.obj bn_mp_copy.obj \ +bn_mp_init_copy.obj bn_mp_abs.obj bn_mp_neg.obj bn_mp_cmp_mag.obj bn_mp_cmp.obj bn_mp_cmp_d.obj \ +bn_mp_rshd.obj bn_mp_lshd.obj bn_mp_mod_2d.obj bn_mp_div_2d.obj bn_mp_mul_2d.obj bn_mp_div_2.obj \ +bn_mp_mul_2.obj bn_s_mp_add.obj bn_s_mp_sub.obj bn_fast_s_mp_mul_digs.obj bn_s_mp_mul_digs.obj \ +bn_fast_s_mp_mul_high_digs.obj bn_s_mp_mul_high_digs.obj bn_fast_s_mp_sqr.obj bn_s_mp_sqr.obj \ +bn_mp_add.obj bn_mp_sub.obj bn_mp_karatsuba_mul.obj bn_mp_mul.obj bn_mp_karatsuba_sqr.obj \ +bn_mp_sqr.obj bn_mp_div.obj bn_mp_mod.obj bn_mp_add_d.obj bn_mp_sub_d.obj bn_mp_mul_d.obj \ +bn_mp_div_d.obj bn_mp_mod_d.obj bn_mp_expt_d.obj bn_mp_addmod.obj bn_mp_submod.obj \ +bn_mp_mulmod.obj bn_mp_sqrmod.obj bn_mp_gcd.obj bn_mp_lcm.obj bn_fast_mp_invmod.obj bn_mp_invmod.obj \ +bn_mp_reduce.obj bn_mp_montgomery_setup.obj bn_fast_mp_montgomery_reduce.obj bn_mp_montgomery_reduce.obj \ +bn_mp_exptmod_fast.obj bn_mp_exptmod.obj bn_mp_2expt.obj bn_mp_n_root.obj bn_mp_jacobi.obj bn_reverse.obj \ +bn_mp_count_bits.obj bn_mp_read_unsigned_bin.obj bn_mp_read_signed_bin.obj bn_mp_to_unsigned_bin.obj \ +bn_mp_to_signed_bin.obj bn_mp_unsigned_bin_size.obj bn_mp_signed_bin_size.obj \ +bn_mp_xor.obj bn_mp_and.obj bn_mp_or.obj bn_mp_rand.obj bn_mp_montgomery_calc_normalization.obj \ +bn_mp_prime_is_divisible.obj bn_prime_tab.obj bn_mp_prime_fermat.obj bn_mp_prime_miller_rabin.obj \ +bn_mp_prime_is_prime.obj bn_mp_prime_next_prime.obj bn_mp_dr_reduce.obj \ +bn_mp_dr_is_modulus.obj bn_mp_dr_setup.obj bn_mp_reduce_setup.obj \ +bn_mp_toom_mul.obj bn_mp_toom_sqr.obj bn_mp_div_3.obj bn_s_mp_exptmod.obj \ +bn_mp_reduce_2k.obj bn_mp_reduce_is_2k.obj bn_mp_reduce_2k_setup.obj \ +bn_mp_radix_smap.obj bn_mp_read_radix.obj bn_mp_toradix.obj bn_mp_radix_size.obj \ +bn_mp_fread.obj bn_mp_fwrite.obj bn_mp_cnt_lsb.obj bn_error.obj \ +bn_mp_init_multi.obj bn_mp_clear_multi.obj bn_prime_sizes_tab.obj bn_mp_exteuclid.obj bn_mp_toradix_n.obj \ +bn_mp_prime_random_ex.obj bn_mp_get_int.obj bn_mp_sqrt.obj bn_mp_is_square.obj + +library: $(OBJECTS) + lib /out:tommath.lib $(OBJECTS) diff --git a/mtest/logtab.h b/mtest/logtab.h new file mode 100644 index 0000000..68462bd --- /dev/null +++ b/mtest/logtab.h @@ -0,0 +1,20 @@ +const float s_logv_2[] = { + 0.000000000, 0.000000000, 1.000000000, 0.630929754, /* 0 1 2 3 */ + 0.500000000, 0.430676558, 0.386852807, 0.356207187, /* 4 5 6 7 */ + 0.333333333, 0.315464877, 0.301029996, 0.289064826, /* 8 9 10 11 */ + 0.278942946, 0.270238154, 0.262649535, 0.255958025, /* 12 13 14 15 */ + 0.250000000, 0.244650542, 0.239812467, 0.235408913, /* 16 17 18 19 */ + 0.231378213, 0.227670249, 0.224243824, 0.221064729, /* 20 21 22 23 */ + 0.218104292, 0.215338279, 0.212746054, 0.210309918, /* 24 25 26 27 */ + 0.208014598, 0.205846832, 0.203795047, 0.201849087, /* 28 29 30 31 */ + 0.200000000, 0.198239863, 0.196561632, 0.194959022, /* 32 33 34 35 */ + 0.193426404, 0.191958720, 0.190551412, 0.189200360, /* 36 37 38 39 */ + 0.187901825, 0.186652411, 0.185449023, 0.184288833, /* 40 41 42 43 */ + 0.183169251, 0.182087900, 0.181042597, 0.180031327, /* 44 45 46 47 */ + 0.179052232, 0.178103594, 0.177183820, 0.176291434, /* 48 49 50 51 */ + 0.175425064, 0.174583430, 0.173765343, 0.172969690, /* 52 53 54 55 */ + 0.172195434, 0.171441601, 0.170707280, 0.169991616, /* 56 57 58 59 */ + 0.169293808, 0.168613099, 0.167948779, 0.167300179, /* 60 61 62 63 */ + 0.166666667 +}; + diff --git a/mtest/mpi-config.h b/mtest/mpi-config.h new file mode 100644 index 0000000..1baf2c2 --- /dev/null +++ b/mtest/mpi-config.h @@ -0,0 +1,86 @@ +/* Default configuration for MPI library */ +/* $Id: mpi-config.h,v 1.8 2000/07/11 04:28:14 sting Exp sting $ */ + +#ifndef MPI_CONFIG_H_ +#define MPI_CONFIG_H_ + +/* + For boolean options, + 0 = no + 1 = yes + + Other options are documented individually. + + */ + +#ifndef MP_IOFUNC +#define MP_IOFUNC 0 /* include mp_print() ? */ +#endif + +#ifndef MP_MODARITH +#define MP_MODARITH 1 /* include modular arithmetic ? */ +#endif + +#ifndef MP_NUMTH +#define MP_NUMTH 1 /* include number theoretic functions? */ +#endif + +#ifndef MP_LOGTAB +#define MP_LOGTAB 1 /* use table of logs instead of log()? */ +#endif + +#ifndef MP_MEMSET +#define MP_MEMSET 1 /* use memset() to zero buffers? */ +#endif + +#ifndef MP_MEMCPY +#define MP_MEMCPY 1 /* use memcpy() to copy buffers? */ +#endif + +#ifndef MP_CRYPTO +#define MP_CRYPTO 1 /* erase memory on free? */ +#endif + +#ifndef MP_ARGCHK +/* + 0 = no parameter checks + 1 = runtime checks, continue execution and return an error to caller + 2 = assertions; dump core on parameter errors + */ +#define MP_ARGCHK 2 /* how to check input arguments */ +#endif + +#ifndef MP_DEBUG +#define MP_DEBUG 0 /* print diagnostic output? */ +#endif + +#ifndef MP_DEFPREC +#define MP_DEFPREC 64 /* default precision, in digits */ +#endif + +#ifndef MP_MACRO +#define MP_MACRO 1 /* use macros for frequent calls? */ +#endif + +#ifndef MP_SQUARE +#define MP_SQUARE 1 /* use separate squaring code? */ +#endif + +#ifndef MP_PTAB_SIZE +/* + When building mpprime.c, we build in a table of small prime + values to use for primality testing. The more you include, + the more space they take up. See primes.c for the possible + values (currently 16, 32, 64, 128, 256, and 6542) + */ +#define MP_PTAB_SIZE 128 /* how many built-in primes? */ +#endif + +#ifndef MP_COMPAT_MACROS +#define MP_COMPAT_MACROS 1 /* define compatibility macros? */ +#endif + +#endif /* ifndef MPI_CONFIG_H_ */ + + +/* crc==3287762869, version==2, Sat Feb 02 06:43:53 2002 */ diff --git a/mtest/mpi-types.h b/mtest/mpi-types.h new file mode 100644 index 0000000..e097188 --- /dev/null +++ b/mtest/mpi-types.h @@ -0,0 +1,16 @@ +/* Type definitions generated by 'types.pl' */ +typedef char mp_sign; +typedef unsigned short mp_digit; /* 2 byte type */ +typedef unsigned int mp_word; /* 4 byte type */ +typedef unsigned int mp_size; +typedef int mp_err; + +#define MP_DIGIT_BIT (CHAR_BIT*sizeof(mp_digit)) +#define MP_DIGIT_MAX USHRT_MAX +#define MP_WORD_BIT (CHAR_BIT*sizeof(mp_word)) +#define MP_WORD_MAX UINT_MAX + +#define MP_DIGIT_SIZE 2 +#define DIGIT_FMT "%04X" +#define RADIX (MP_DIGIT_MAX+1) + diff --git a/mtest/mpi.c b/mtest/mpi.c new file mode 100644 index 0000000..f7688f3 --- /dev/null +++ b/mtest/mpi.c @@ -0,0 +1,3981 @@ +/* + mpi.c + + by Michael J. Fromberger + Copyright (C) 1998 Michael J. Fromberger, All Rights Reserved + + Arbitrary precision integer arithmetic library + + $Id: mpi.c,v 1.22 2001/09/14 15:11:20 sting Exp sting $ + */ + +#include "mpi.h" +#include +#include +#include + +#if MP_DEBUG +#include + +#define DIAG(T,V) {fprintf(stderr,T);mp_print(V,stderr);fputc('\n',stderr);} +#else +#define DIAG(T,V) +#endif + +/* + If MP_LOGTAB is not defined, use the math library to compute the + logarithms on the fly. Otherwise, use the static table below. + Pick which works best for your system. + */ +#if MP_LOGTAB + +/* {{{ s_logv_2[] - log table for 2 in various bases */ + +/* + A table of the logs of 2 for various bases (the 0 and 1 entries of + this table are meaningless and should not be referenced). + + This table is used to compute output lengths for the mp_toradix() + function. Since a number n in radix r takes up about log_r(n) + digits, we estimate the output size by taking the least integer + greater than log_r(n), where: + + log_r(n) = log_2(n) * log_r(2) + + This table, therefore, is a table of log_r(2) for 2 <= r <= 36, + which are the output bases supported. + */ + +#include "logtab.h" + +/* }}} */ +#define LOG_V_2(R) s_logv_2[(R)] + +#else + +#include +#define LOG_V_2(R) (log(2.0)/log(R)) + +#endif + +/* Default precision for newly created mp_int's */ +static unsigned int s_mp_defprec = MP_DEFPREC; + +/* {{{ Digit arithmetic macros */ + +/* + When adding and multiplying digits, the results can be larger than + can be contained in an mp_digit. Thus, an mp_word is used. These + macros mask off the upper and lower digits of the mp_word (the + mp_word may be more than 2 mp_digits wide, but we only concern + ourselves with the low-order 2 mp_digits) + + If your mp_word DOES have more than 2 mp_digits, you need to + uncomment the first line, and comment out the second. + */ + +/* #define CARRYOUT(W) (((W)>>DIGIT_BIT)&MP_DIGIT_MAX) */ +#define CARRYOUT(W) ((W)>>DIGIT_BIT) +#define ACCUM(W) ((W)&MP_DIGIT_MAX) + +/* }}} */ + +/* {{{ Comparison constants */ + +#define MP_LT -1 +#define MP_EQ 0 +#define MP_GT 1 + +/* }}} */ + +/* {{{ Constant strings */ + +/* Constant strings returned by mp_strerror() */ +static const char *mp_err_string[] = { + "unknown result code", /* say what? */ + "boolean true", /* MP_OKAY, MP_YES */ + "boolean false", /* MP_NO */ + "out of memory", /* MP_MEM */ + "argument out of range", /* MP_RANGE */ + "invalid input parameter", /* MP_BADARG */ + "result is undefined" /* MP_UNDEF */ +}; + +/* Value to digit maps for radix conversion */ + +/* s_dmap_1 - standard digits and letters */ +static const char *s_dmap_1 = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; + +#if 0 +/* s_dmap_2 - base64 ordering for digits */ +static const char *s_dmap_2 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#endif + +/* }}} */ + +/* {{{ Static function declarations */ + +/* + If MP_MACRO is false, these will be defined as actual functions; + otherwise, suitable macro definitions will be used. This works + around the fact that ANSI C89 doesn't support an 'inline' keyword + (although I hear C9x will ... about bloody time). At present, the + macro definitions are identical to the function bodies, but they'll + expand in place, instead of generating a function call. + + I chose these particular functions to be made into macros because + some profiling showed they are called a lot on a typical workload, + and yet they are primarily housekeeping. + */ +#if MP_MACRO == 0 + void s_mp_setz(mp_digit *dp, mp_size count); /* zero digits */ + void s_mp_copy(mp_digit *sp, mp_digit *dp, mp_size count); /* copy */ + void *s_mp_alloc(size_t nb, size_t ni); /* general allocator */ + void s_mp_free(void *ptr); /* general free function */ +#else + + /* Even if these are defined as macros, we need to respect the settings + of the MP_MEMSET and MP_MEMCPY configuration options... + */ + #if MP_MEMSET == 0 + #define s_mp_setz(dp, count) \ + {int ix;for(ix=0;ix<(count);ix++)(dp)[ix]=0;} + #else + #define s_mp_setz(dp, count) memset(dp, 0, (count) * sizeof(mp_digit)) + #endif /* MP_MEMSET */ + + #if MP_MEMCPY == 0 + #define s_mp_copy(sp, dp, count) \ + {int ix;for(ix=0;ix<(count);ix++)(dp)[ix]=(sp)[ix];} + #else + #define s_mp_copy(sp, dp, count) memcpy(dp, sp, (count) * sizeof(mp_digit)) + #endif /* MP_MEMCPY */ + + #define s_mp_alloc(nb, ni) calloc(nb, ni) + #define s_mp_free(ptr) {if(ptr) free(ptr);} +#endif /* MP_MACRO */ + +mp_err s_mp_grow(mp_int *mp, mp_size min); /* increase allocated size */ +mp_err s_mp_pad(mp_int *mp, mp_size min); /* left pad with zeroes */ + +void s_mp_clamp(mp_int *mp); /* clip leading zeroes */ + +void s_mp_exch(mp_int *a, mp_int *b); /* swap a and b in place */ + +mp_err s_mp_lshd(mp_int *mp, mp_size p); /* left-shift by p digits */ +void s_mp_rshd(mp_int *mp, mp_size p); /* right-shift by p digits */ +void s_mp_div_2d(mp_int *mp, mp_digit d); /* divide by 2^d in place */ +void s_mp_mod_2d(mp_int *mp, mp_digit d); /* modulo 2^d in place */ +mp_err s_mp_mul_2d(mp_int *mp, mp_digit d); /* multiply by 2^d in place*/ +void s_mp_div_2(mp_int *mp); /* divide by 2 in place */ +mp_err s_mp_mul_2(mp_int *mp); /* multiply by 2 in place */ +mp_digit s_mp_norm(mp_int *a, mp_int *b); /* normalize for division */ +mp_err s_mp_add_d(mp_int *mp, mp_digit d); /* unsigned digit addition */ +mp_err s_mp_sub_d(mp_int *mp, mp_digit d); /* unsigned digit subtract */ +mp_err s_mp_mul_d(mp_int *mp, mp_digit d); /* unsigned digit multiply */ +mp_err s_mp_div_d(mp_int *mp, mp_digit d, mp_digit *r); + /* unsigned digit divide */ +mp_err s_mp_reduce(mp_int *x, mp_int *m, mp_int *mu); + /* Barrett reduction */ +mp_err s_mp_add(mp_int *a, mp_int *b); /* magnitude addition */ +mp_err s_mp_sub(mp_int *a, mp_int *b); /* magnitude subtract */ +mp_err s_mp_mul(mp_int *a, mp_int *b); /* magnitude multiply */ +#if 0 +void s_mp_kmul(mp_digit *a, mp_digit *b, mp_digit *out, mp_size len); + /* multiply buffers in place */ +#endif +#if MP_SQUARE +mp_err s_mp_sqr(mp_int *a); /* magnitude square */ +#else +#define s_mp_sqr(a) s_mp_mul(a, a) +#endif +mp_err s_mp_div(mp_int *a, mp_int *b); /* magnitude divide */ +mp_err s_mp_2expt(mp_int *a, mp_digit k); /* a = 2^k */ +int s_mp_cmp(mp_int *a, mp_int *b); /* magnitude comparison */ +int s_mp_cmp_d(mp_int *a, mp_digit d); /* magnitude digit compare */ +int s_mp_ispow2(mp_int *v); /* is v a power of 2? */ +int s_mp_ispow2d(mp_digit d); /* is d a power of 2? */ + +int s_mp_tovalue(char ch, int r); /* convert ch to value */ +char s_mp_todigit(int val, int r, int low); /* convert val to digit */ +int s_mp_outlen(int bits, int r); /* output length in bytes */ + +/* }}} */ + +/* {{{ Default precision manipulation */ + +unsigned int mp_get_prec(void) +{ + return s_mp_defprec; + +} /* end mp_get_prec() */ + +void mp_set_prec(unsigned int prec) +{ + if(prec == 0) + s_mp_defprec = MP_DEFPREC; + else + s_mp_defprec = prec; + +} /* end mp_set_prec() */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ mp_init(mp) */ + +/* + mp_init(mp) + + Initialize a new zero-valued mp_int. Returns MP_OKAY if successful, + MP_MEM if memory could not be allocated for the structure. + */ + +mp_err mp_init(mp_int *mp) +{ + return mp_init_size(mp, s_mp_defprec); + +} /* end mp_init() */ + +/* }}} */ + +/* {{{ mp_init_array(mp[], count) */ + +mp_err mp_init_array(mp_int mp[], int count) +{ + mp_err res; + int pos; + + ARGCHK(mp !=NULL && count > 0, MP_BADARG); + + for(pos = 0; pos < count; ++pos) { + if((res = mp_init(&mp[pos])) != MP_OKAY) + goto CLEANUP; + } + + return MP_OKAY; + + CLEANUP: + while(--pos >= 0) + mp_clear(&mp[pos]); + + return res; + +} /* end mp_init_array() */ + +/* }}} */ + +/* {{{ mp_init_size(mp, prec) */ + +/* + mp_init_size(mp, prec) + + Initialize a new zero-valued mp_int with at least the given + precision; returns MP_OKAY if successful, or MP_MEM if memory could + not be allocated for the structure. + */ + +mp_err mp_init_size(mp_int *mp, mp_size prec) +{ + ARGCHK(mp != NULL && prec > 0, MP_BADARG); + + if((DIGITS(mp) = s_mp_alloc(prec, sizeof(mp_digit))) == NULL) + return MP_MEM; + + SIGN(mp) = MP_ZPOS; + USED(mp) = 1; + ALLOC(mp) = prec; + + return MP_OKAY; + +} /* end mp_init_size() */ + +/* }}} */ + +/* {{{ mp_init_copy(mp, from) */ + +/* + mp_init_copy(mp, from) + + Initialize mp as an exact copy of from. Returns MP_OKAY if + successful, MP_MEM if memory could not be allocated for the new + structure. + */ + +mp_err mp_init_copy(mp_int *mp, mp_int *from) +{ + ARGCHK(mp != NULL && from != NULL, MP_BADARG); + + if(mp == from) + return MP_OKAY; + + if((DIGITS(mp) = s_mp_alloc(USED(from), sizeof(mp_digit))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(from), DIGITS(mp), USED(from)); + USED(mp) = USED(from); + ALLOC(mp) = USED(from); + SIGN(mp) = SIGN(from); + + return MP_OKAY; + +} /* end mp_init_copy() */ + +/* }}} */ + +/* {{{ mp_copy(from, to) */ + +/* + mp_copy(from, to) + + Copies the mp_int 'from' to the mp_int 'to'. It is presumed that + 'to' has already been initialized (if not, use mp_init_copy() + instead). If 'from' and 'to' are identical, nothing happens. + */ + +mp_err mp_copy(mp_int *from, mp_int *to) +{ + ARGCHK(from != NULL && to != NULL, MP_BADARG); + + if(from == to) + return MP_OKAY; + + { /* copy */ + mp_digit *tmp; + + /* + If the allocated buffer in 'to' already has enough space to hold + all the used digits of 'from', we'll re-use it to avoid hitting + the memory allocater more than necessary; otherwise, we'd have + to grow anyway, so we just allocate a hunk and make the copy as + usual + */ + if(ALLOC(to) >= USED(from)) { + s_mp_setz(DIGITS(to) + USED(from), ALLOC(to) - USED(from)); + s_mp_copy(DIGITS(from), DIGITS(to), USED(from)); + + } else { + if((tmp = s_mp_alloc(USED(from), sizeof(mp_digit))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(from), tmp, USED(from)); + + if(DIGITS(to) != NULL) { +#if MP_CRYPTO + s_mp_setz(DIGITS(to), ALLOC(to)); +#endif + s_mp_free(DIGITS(to)); + } + + DIGITS(to) = tmp; + ALLOC(to) = USED(from); + } + + /* Copy the precision and sign from the original */ + USED(to) = USED(from); + SIGN(to) = SIGN(from); + } /* end copy */ + + return MP_OKAY; + +} /* end mp_copy() */ + +/* }}} */ + +/* {{{ mp_exch(mp1, mp2) */ + +/* + mp_exch(mp1, mp2) + + Exchange mp1 and mp2 without allocating any intermediate memory + (well, unless you count the stack space needed for this call and the + locals it creates...). This cannot fail. + */ + +void mp_exch(mp_int *mp1, mp_int *mp2) +{ +#if MP_ARGCHK == 2 + assert(mp1 != NULL && mp2 != NULL); +#else + if(mp1 == NULL || mp2 == NULL) + return; +#endif + + s_mp_exch(mp1, mp2); + +} /* end mp_exch() */ + +/* }}} */ + +/* {{{ mp_clear(mp) */ + +/* + mp_clear(mp) + + Release the storage used by an mp_int, and void its fields so that + if someone calls mp_clear() again for the same int later, we won't + get tollchocked. + */ + +void mp_clear(mp_int *mp) +{ + if(mp == NULL) + return; + + if(DIGITS(mp) != NULL) { +#if MP_CRYPTO + s_mp_setz(DIGITS(mp), ALLOC(mp)); +#endif + s_mp_free(DIGITS(mp)); + DIGITS(mp) = NULL; + } + + USED(mp) = 0; + ALLOC(mp) = 0; + +} /* end mp_clear() */ + +/* }}} */ + +/* {{{ mp_clear_array(mp[], count) */ + +void mp_clear_array(mp_int mp[], int count) +{ + ARGCHK(mp != NULL && count > 0, MP_BADARG); + + while(--count >= 0) + mp_clear(&mp[count]); + +} /* end mp_clear_array() */ + +/* }}} */ + +/* {{{ mp_zero(mp) */ + +/* + mp_zero(mp) + + Set mp to zero. Does not change the allocated size of the structure, + and therefore cannot fail (except on a bad argument, which we ignore) + */ +void mp_zero(mp_int *mp) +{ + if(mp == NULL) + return; + + s_mp_setz(DIGITS(mp), ALLOC(mp)); + USED(mp) = 1; + SIGN(mp) = MP_ZPOS; + +} /* end mp_zero() */ + +/* }}} */ + +/* {{{ mp_set(mp, d) */ + +void mp_set(mp_int *mp, mp_digit d) +{ + if(mp == NULL) + return; + + mp_zero(mp); + DIGIT(mp, 0) = d; + +} /* end mp_set() */ + +/* }}} */ + +/* {{{ mp_set_int(mp, z) */ + +mp_err mp_set_int(mp_int *mp, long z) +{ + int ix; + unsigned long v = abs(z); + mp_err res; + + ARGCHK(mp != NULL, MP_BADARG); + + mp_zero(mp); + if(z == 0) + return MP_OKAY; /* shortcut for zero */ + + for(ix = sizeof(long) - 1; ix >= 0; ix--) { + + if((res = s_mp_mul_2d(mp, CHAR_BIT)) != MP_OKAY) + return res; + + res = s_mp_add_d(mp, + (mp_digit)((v >> (ix * CHAR_BIT)) & UCHAR_MAX)); + if(res != MP_OKAY) + return res; + + } + + if(z < 0) + SIGN(mp) = MP_NEG; + + return MP_OKAY; + +} /* end mp_set_int() */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Digit arithmetic */ + +/* {{{ mp_add_d(a, d, b) */ + +/* + mp_add_d(a, d, b) + + Compute the sum b = a + d, for a single digit d. Respects the sign of + its primary addend (single digits are unsigned anyway). + */ + +mp_err mp_add_d(mp_int *a, mp_digit d, mp_int *b) +{ + mp_err res = MP_OKAY; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if(SIGN(b) == MP_ZPOS) { + res = s_mp_add_d(b, d); + } else if(s_mp_cmp_d(b, d) >= 0) { + res = s_mp_sub_d(b, d); + } else { + SIGN(b) = MP_ZPOS; + + DIGIT(b, 0) = d - DIGIT(b, 0); + } + + return res; + +} /* end mp_add_d() */ + +/* }}} */ + +/* {{{ mp_sub_d(a, d, b) */ + +/* + mp_sub_d(a, d, b) + + Compute the difference b = a - d, for a single digit d. Respects the + sign of its subtrahend (single digits are unsigned anyway). + */ + +mp_err mp_sub_d(mp_int *a, mp_digit d, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if(SIGN(b) == MP_NEG) { + if((res = s_mp_add_d(b, d)) != MP_OKAY) + return res; + + } else if(s_mp_cmp_d(b, d) >= 0) { + if((res = s_mp_sub_d(b, d)) != MP_OKAY) + return res; + + } else { + mp_neg(b, b); + + DIGIT(b, 0) = d - DIGIT(b, 0); + SIGN(b) = MP_NEG; + } + + if(s_mp_cmp_d(b, 0) == 0) + SIGN(b) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_sub_d() */ + +/* }}} */ + +/* {{{ mp_mul_d(a, d, b) */ + +/* + mp_mul_d(a, d, b) + + Compute the product b = a * d, for a single digit d. Respects the sign + of its multiplicand (single digits are unsigned anyway) + */ + +mp_err mp_mul_d(mp_int *a, mp_digit d, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if(d == 0) { + mp_zero(b); + return MP_OKAY; + } + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + res = s_mp_mul_d(b, d); + + return res; + +} /* end mp_mul_d() */ + +/* }}} */ + +/* {{{ mp_mul_2(a, c) */ + +mp_err mp_mul_2(mp_int *a, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + return s_mp_mul_2(c); + +} /* end mp_mul_2() */ + +/* }}} */ + +/* {{{ mp_div_d(a, d, q, r) */ + +/* + mp_div_d(a, d, q, r) + + Compute the quotient q = a / d and remainder r = a mod d, for a + single digit d. Respects the sign of its divisor (single digits are + unsigned anyway). + */ + +mp_err mp_div_d(mp_int *a, mp_digit d, mp_int *q, mp_digit *r) +{ + mp_err res; + mp_digit rem; + int pow; + + ARGCHK(a != NULL, MP_BADARG); + + if(d == 0) + return MP_RANGE; + + /* Shortcut for powers of two ... */ + if((pow = s_mp_ispow2d(d)) >= 0) { + mp_digit mask; + + mask = (1 << pow) - 1; + rem = DIGIT(a, 0) & mask; + + if(q) { + mp_copy(a, q); + s_mp_div_2d(q, pow); + } + + if(r) + *r = rem; + + return MP_OKAY; + } + + /* + If the quotient is actually going to be returned, we'll try to + avoid hitting the memory allocator by copying the dividend into it + and doing the division there. This can't be any _worse_ than + always copying, and will sometimes be better (since it won't make + another copy) + + If it's not going to be returned, we need to allocate a temporary + to hold the quotient, which will just be discarded. + */ + if(q) { + if((res = mp_copy(a, q)) != MP_OKAY) + return res; + + res = s_mp_div_d(q, d, &rem); + if(s_mp_cmp_d(q, 0) == MP_EQ) + SIGN(q) = MP_ZPOS; + + } else { + mp_int qp; + + if((res = mp_init_copy(&qp, a)) != MP_OKAY) + return res; + + res = s_mp_div_d(&qp, d, &rem); + if(s_mp_cmp_d(&qp, 0) == 0) + SIGN(&qp) = MP_ZPOS; + + mp_clear(&qp); + } + + if(r) + *r = rem; + + return res; + +} /* end mp_div_d() */ + +/* }}} */ + +/* {{{ mp_div_2(a, c) */ + +/* + mp_div_2(a, c) + + Compute c = a / 2, disregarding the remainder. + */ + +mp_err mp_div_2(mp_int *a, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + s_mp_div_2(c); + + return MP_OKAY; + +} /* end mp_div_2() */ + +/* }}} */ + +/* {{{ mp_expt_d(a, d, b) */ + +mp_err mp_expt_d(mp_int *a, mp_digit d, mp_int *c) +{ + mp_int s, x; + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_init(&s)) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + DIGIT(&s, 0) = 1; + + while(d != 0) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_expt_d() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Full arithmetic */ + +/* {{{ mp_abs(a, b) */ + +/* + mp_abs(a, b) + + Compute b = |a|. 'a' and 'b' may be identical. + */ + +mp_err mp_abs(mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + SIGN(b) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_abs() */ + +/* }}} */ + +/* {{{ mp_neg(a, b) */ + +/* + mp_neg(a, b) + + Compute b = -a. 'a' and 'b' may be identical. + */ + +mp_err mp_neg(mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if(s_mp_cmp_d(b, 0) == MP_EQ) + SIGN(b) = MP_ZPOS; + else + SIGN(b) = (SIGN(b) == MP_NEG) ? MP_ZPOS : MP_NEG; + + return MP_OKAY; + +} /* end mp_neg() */ + +/* }}} */ + +/* {{{ mp_add(a, b, c) */ + +/* + mp_add(a, b, c) + + Compute c = a + b. All parameters may be identical. + */ + +mp_err mp_add(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + int cmp; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(SIGN(a) == SIGN(b)) { /* same sign: add values, keep sign */ + + /* Commutativity of addition lets us do this in either order, + so we avoid having to use a temporary even if the result + is supposed to replace the output + */ + if(c == b) { + if((res = s_mp_add(c, a)) != MP_OKAY) + return res; + } else { + if(c != a && (res = mp_copy(a, c)) != MP_OKAY) + return res; + + if((res = s_mp_add(c, b)) != MP_OKAY) + return res; + } + + } else if((cmp = s_mp_cmp(a, b)) > 0) { /* different sign: a > b */ + + /* If the output is going to be clobbered, we will use a temporary + variable; otherwise, we'll do it without touching the memory + allocator at all, if possible + */ + if(c == b) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + if((res = s_mp_sub(&tmp, b)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + + if(c != a && (res = mp_copy(a, c)) != MP_OKAY) + return res; + if((res = s_mp_sub(c, b)) != MP_OKAY) + return res; + + } + + } else if(cmp == 0) { /* different sign, a == b */ + + mp_zero(c); + return MP_OKAY; + + } else { /* different sign: a < b */ + + /* See above... */ + if(c == a) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, b)) != MP_OKAY) + return res; + if((res = s_mp_sub(&tmp, a)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + + if(c != b && (res = mp_copy(b, c)) != MP_OKAY) + return res; + if((res = s_mp_sub(c, a)) != MP_OKAY) + return res; + + } + } + + if(USED(c) == 1 && DIGIT(c, 0) == 0) + SIGN(c) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_add() */ + +/* }}} */ + +/* {{{ mp_sub(a, b, c) */ + +/* + mp_sub(a, b, c) + + Compute c = a - b. All parameters may be identical. + */ + +mp_err mp_sub(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + int cmp; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(SIGN(a) != SIGN(b)) { + if(c == a) { + if((res = s_mp_add(c, b)) != MP_OKAY) + return res; + } else { + if(c != b && ((res = mp_copy(b, c)) != MP_OKAY)) + return res; + if((res = s_mp_add(c, a)) != MP_OKAY) + return res; + SIGN(c) = SIGN(a); + } + + } else if((cmp = s_mp_cmp(a, b)) > 0) { /* Same sign, a > b */ + if(c == b) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + if((res = s_mp_sub(&tmp, b)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + if(c != a && ((res = mp_copy(a, c)) != MP_OKAY)) + return res; + + if((res = s_mp_sub(c, b)) != MP_OKAY) + return res; + } + + } else if(cmp == 0) { /* Same sign, equal magnitude */ + mp_zero(c); + return MP_OKAY; + + } else { /* Same sign, b > a */ + if(c == a) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, b)) != MP_OKAY) + return res; + + if((res = s_mp_sub(&tmp, a)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + if(c != b && ((res = mp_copy(b, c)) != MP_OKAY)) + return res; + + if((res = s_mp_sub(c, a)) != MP_OKAY) + return res; + } + + SIGN(c) = !SIGN(b); + } + + if(USED(c) == 1 && DIGIT(c, 0) == 0) + SIGN(c) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_sub() */ + +/* }}} */ + +/* {{{ mp_mul(a, b, c) */ + +/* + mp_mul(a, b, c) + + Compute c = a * b. All parameters may be identical. + */ + +mp_err mp_mul(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + mp_sign sgn; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + sgn = (SIGN(a) == SIGN(b)) ? MP_ZPOS : MP_NEG; + + if(c == b) { + if((res = s_mp_mul(c, a)) != MP_OKAY) + return res; + + } else { + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + if((res = s_mp_mul(c, b)) != MP_OKAY) + return res; + } + + if(sgn == MP_ZPOS || s_mp_cmp_d(c, 0) == MP_EQ) + SIGN(c) = MP_ZPOS; + else + SIGN(c) = sgn; + + return MP_OKAY; + +} /* end mp_mul() */ + +/* }}} */ + +/* {{{ mp_mul_2d(a, d, c) */ + +/* + mp_mul_2d(a, d, c) + + Compute c = a * 2^d. a may be the same as c. + */ + +mp_err mp_mul_2d(mp_int *a, mp_digit d, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + if(d == 0) + return MP_OKAY; + + return s_mp_mul_2d(c, d); + +} /* end mp_mul() */ + +/* }}} */ + +/* {{{ mp_sqr(a, b) */ + +#if MP_SQUARE +mp_err mp_sqr(mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if((res = s_mp_sqr(b)) != MP_OKAY) + return res; + + SIGN(b) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_sqr() */ +#endif + +/* }}} */ + +/* {{{ mp_div(a, b, q, r) */ + +/* + mp_div(a, b, q, r) + + Compute q = a / b and r = a mod b. Input parameters may be re-used + as output parameters. If q or r is NULL, that portion of the + computation will be discarded (although it will still be computed) + + Pay no attention to the hacker behind the curtain. + */ + +mp_err mp_div(mp_int *a, mp_int *b, mp_int *q, mp_int *r) +{ + mp_err res; + mp_int qtmp, rtmp; + int cmp; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if(mp_cmp_z(b) == MP_EQ) + return MP_RANGE; + + /* If a <= b, we can compute the solution without division, and + avoid any memory allocation + */ + if((cmp = s_mp_cmp(a, b)) < 0) { + if(r) { + if((res = mp_copy(a, r)) != MP_OKAY) + return res; + } + + if(q) + mp_zero(q); + + return MP_OKAY; + + } else if(cmp == 0) { + + /* Set quotient to 1, with appropriate sign */ + if(q) { + int qneg = (SIGN(a) != SIGN(b)); + + mp_set(q, 1); + if(qneg) + SIGN(q) = MP_NEG; + } + + if(r) + mp_zero(r); + + return MP_OKAY; + } + + /* If we get here, it means we actually have to do some division */ + + /* Set up some temporaries... */ + if((res = mp_init_copy(&qtmp, a)) != MP_OKAY) + return res; + if((res = mp_init_copy(&rtmp, b)) != MP_OKAY) + goto CLEANUP; + + if((res = s_mp_div(&qtmp, &rtmp)) != MP_OKAY) + goto CLEANUP; + + /* Compute the signs for the output */ + SIGN(&rtmp) = SIGN(a); /* Sr = Sa */ + if(SIGN(a) == SIGN(b)) + SIGN(&qtmp) = MP_ZPOS; /* Sq = MP_ZPOS if Sa = Sb */ + else + SIGN(&qtmp) = MP_NEG; /* Sq = MP_NEG if Sa != Sb */ + + if(s_mp_cmp_d(&qtmp, 0) == MP_EQ) + SIGN(&qtmp) = MP_ZPOS; + if(s_mp_cmp_d(&rtmp, 0) == MP_EQ) + SIGN(&rtmp) = MP_ZPOS; + + /* Copy output, if it is needed */ + if(q) + s_mp_exch(&qtmp, q); + + if(r) + s_mp_exch(&rtmp, r); + +CLEANUP: + mp_clear(&rtmp); + mp_clear(&qtmp); + + return res; + +} /* end mp_div() */ + +/* }}} */ + +/* {{{ mp_div_2d(a, d, q, r) */ + +mp_err mp_div_2d(mp_int *a, mp_digit d, mp_int *q, mp_int *r) +{ + mp_err res; + + ARGCHK(a != NULL, MP_BADARG); + + if(q) { + if((res = mp_copy(a, q)) != MP_OKAY) + return res; + + s_mp_div_2d(q, d); + } + + if(r) { + if((res = mp_copy(a, r)) != MP_OKAY) + return res; + + s_mp_mod_2d(r, d); + } + + return MP_OKAY; + +} /* end mp_div_2d() */ + +/* }}} */ + +/* {{{ mp_expt(a, b, c) */ + +/* + mp_expt(a, b, c) + + Compute c = a ** b, that is, raise a to the b power. Uses a + standard iterative square-and-multiply technique. + */ + +mp_err mp_expt(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int s, x; + mp_err res; + mp_digit d; + int dig, bit; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(b) < 0) + return MP_RANGE; + + if((res = mp_init(&s)) != MP_OKAY) + return res; + + mp_set(&s, 1); + + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + /* Loop over low-order digits in ascending order */ + for(dig = 0; dig < (USED(b) - 1); dig++) { + d = DIGIT(b, dig); + + /* Loop over bits of each non-maximal digit */ + for(bit = 0; bit < DIGIT_BIT; bit++) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + } + + /* Consider now the last digit... */ + d = DIGIT(b, dig); + + while(d) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + + if(mp_iseven(b)) + SIGN(&s) = SIGN(a); + + res = mp_copy(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_expt() */ + +/* }}} */ + +/* {{{ mp_2expt(a, k) */ + +/* Compute a = 2^k */ + +mp_err mp_2expt(mp_int *a, mp_digit k) +{ + ARGCHK(a != NULL, MP_BADARG); + + return s_mp_2expt(a, k); + +} /* end mp_2expt() */ + +/* }}} */ + +/* {{{ mp_mod(a, m, c) */ + +/* + mp_mod(a, m, c) + + Compute c = a (mod m). Result will always be 0 <= c < m. + */ + +mp_err mp_mod(mp_int *a, mp_int *m, mp_int *c) +{ + mp_err res; + int mag; + + ARGCHK(a != NULL && m != NULL && c != NULL, MP_BADARG); + + if(SIGN(m) == MP_NEG) + return MP_RANGE; + + /* + If |a| > m, we need to divide to get the remainder and take the + absolute value. + + If |a| < m, we don't need to do any division, just copy and adjust + the sign (if a is negative). + + If |a| == m, we can simply set the result to zero. + + This order is intended to minimize the average path length of the + comparison chain on common workloads -- the most frequent cases are + that |a| != m, so we do those first. + */ + if((mag = s_mp_cmp(a, m)) > 0) { + if((res = mp_div(a, m, NULL, c)) != MP_OKAY) + return res; + + if(SIGN(c) == MP_NEG) { + if((res = mp_add(c, m, c)) != MP_OKAY) + return res; + } + + } else if(mag < 0) { + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + if(mp_cmp_z(a) < 0) { + if((res = mp_add(c, m, c)) != MP_OKAY) + return res; + + } + + } else { + mp_zero(c); + + } + + return MP_OKAY; + +} /* end mp_mod() */ + +/* }}} */ + +/* {{{ mp_mod_d(a, d, c) */ + +/* + mp_mod_d(a, d, c) + + Compute c = a (mod d). Result will always be 0 <= c < d + */ +mp_err mp_mod_d(mp_int *a, mp_digit d, mp_digit *c) +{ + mp_err res; + mp_digit rem; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if(s_mp_cmp_d(a, d) > 0) { + if((res = mp_div_d(a, d, NULL, &rem)) != MP_OKAY) + return res; + + } else { + if(SIGN(a) == MP_NEG) + rem = d - DIGIT(a, 0); + else + rem = DIGIT(a, 0); + } + + if(c) + *c = rem; + + return MP_OKAY; + +} /* end mp_mod_d() */ + +/* }}} */ + +/* {{{ mp_sqrt(a, b) */ + +/* + mp_sqrt(a, b) + + Compute the integer square root of a, and store the result in b. + Uses an integer-arithmetic version of Newton's iterative linear + approximation technique to determine this value; the result has the + following two properties: + + b^2 <= a + (b+1)^2 >= a + + It is a range error to pass a negative value. + */ +mp_err mp_sqrt(mp_int *a, mp_int *b) +{ + mp_int x, t; + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + /* Cannot take square root of a negative value */ + if(SIGN(a) == MP_NEG) + return MP_RANGE; + + /* Special cases for zero and one, trivial */ + if(mp_cmp_d(a, 0) == MP_EQ || mp_cmp_d(a, 1) == MP_EQ) + return mp_copy(a, b); + + /* Initialize the temporaries we'll use below */ + if((res = mp_init_size(&t, USED(a))) != MP_OKAY) + return res; + + /* Compute an initial guess for the iteration as a itself */ + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + +s_mp_rshd(&x, (USED(&x)/2)+1); +mp_add_d(&x, 1, &x); + + for(;;) { + /* t = (x * x) - a */ + mp_copy(&x, &t); /* can't fail, t is big enough for original x */ + if((res = mp_sqr(&t, &t)) != MP_OKAY || + (res = mp_sub(&t, a, &t)) != MP_OKAY) + goto CLEANUP; + + /* t = t / 2x */ + s_mp_mul_2(&x); + if((res = mp_div(&t, &x, &t, NULL)) != MP_OKAY) + goto CLEANUP; + s_mp_div_2(&x); + + /* Terminate the loop, if the quotient is zero */ + if(mp_cmp_z(&t) == MP_EQ) + break; + + /* x = x - t */ + if((res = mp_sub(&x, &t, &x)) != MP_OKAY) + goto CLEANUP; + + } + + /* Copy result to output parameter */ + mp_sub_d(&x, 1, &x); + s_mp_exch(&x, b); + + CLEANUP: + mp_clear(&x); + X: + mp_clear(&t); + + return res; + +} /* end mp_sqrt() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Modular arithmetic */ + +#if MP_MODARITH +/* {{{ mp_addmod(a, b, m, c) */ + +/* + mp_addmod(a, b, m, c) + + Compute c = (a + b) mod m + */ + +mp_err mp_addmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_add(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_submod(a, b, m, c) */ + +/* + mp_submod(a, b, m, c) + + Compute c = (a - b) mod m + */ + +mp_err mp_submod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_sub(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_mulmod(a, b, m, c) */ + +/* + mp_mulmod(a, b, m, c) + + Compute c = (a * b) mod m + */ + +mp_err mp_mulmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_mul(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_sqrmod(a, m, c) */ + +#if MP_SQUARE +mp_err mp_sqrmod(mp_int *a, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_sqr(a, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} /* end mp_sqrmod() */ +#endif + +/* }}} */ + +/* {{{ mp_exptmod(a, b, m, c) */ + +/* + mp_exptmod(a, b, m, c) + + Compute c = (a ** b) mod m. Uses a standard square-and-multiply + method with modular reductions at each step. (This is basically the + same code as mp_expt(), except for the addition of the reductions) + + The modular reductions are done using Barrett's algorithm (see + s_mp_reduce() below for details) + */ + +mp_err mp_exptmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_int s, x, mu; + mp_err res; + mp_digit d, *db = DIGITS(b); + mp_size ub = USED(b); + int dig, bit; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(b) < 0 || mp_cmp_z(m) <= 0) + return MP_RANGE; + + if((res = mp_init(&s)) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + if((res = mp_mod(&x, m, &x)) != MP_OKAY || + (res = mp_init(&mu)) != MP_OKAY) + goto MU; + + mp_set(&s, 1); + + /* mu = b^2k / m */ + s_mp_add_d(&mu, 1); + s_mp_lshd(&mu, 2 * USED(m)); + if((res = mp_div(&mu, m, &mu, NULL)) != MP_OKAY) + goto CLEANUP; + + /* Loop over digits of b in ascending order, except highest order */ + for(dig = 0; dig < (ub - 1); dig++) { + d = *db++; + + /* Loop over the bits of the lower-order digits */ + for(bit = 0; bit < DIGIT_BIT; bit++) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&s, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&x, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + } + + /* Now do the last digit... */ + d = *db; + + while(d) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&s, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&x, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + + CLEANUP: + mp_clear(&mu); + MU: + mp_clear(&x); + X: + mp_clear(&s); + + return res; + +} /* end mp_exptmod() */ + +/* }}} */ + +/* {{{ mp_exptmod_d(a, d, m, c) */ + +mp_err mp_exptmod_d(mp_int *a, mp_digit d, mp_int *m, mp_int *c) +{ + mp_int s, x; + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_init(&s)) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + mp_set(&s, 1); + + while(d != 0) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY || + (res = mp_mod(&s, m, &s)) != MP_OKAY) + goto CLEANUP; + } + + d /= 2; + + if((res = s_mp_sqr(&x)) != MP_OKAY || + (res = mp_mod(&x, m, &x)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_exptmod_d() */ + +/* }}} */ +#endif /* if MP_MODARITH */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Comparison functions */ + +/* {{{ mp_cmp_z(a) */ + +/* + mp_cmp_z(a) + + Compare a <=> 0. Returns <0 if a<0, 0 if a=0, >0 if a>0. + */ + +int mp_cmp_z(mp_int *a) +{ + if(SIGN(a) == MP_NEG) + return MP_LT; + else if(USED(a) == 1 && DIGIT(a, 0) == 0) + return MP_EQ; + else + return MP_GT; + +} /* end mp_cmp_z() */ + +/* }}} */ + +/* {{{ mp_cmp_d(a, d) */ + +/* + mp_cmp_d(a, d) + + Compare a <=> d. Returns <0 if a0 if a>d + */ + +int mp_cmp_d(mp_int *a, mp_digit d) +{ + ARGCHK(a != NULL, MP_EQ); + + if(SIGN(a) == MP_NEG) + return MP_LT; + + return s_mp_cmp_d(a, d); + +} /* end mp_cmp_d() */ + +/* }}} */ + +/* {{{ mp_cmp(a, b) */ + +int mp_cmp(mp_int *a, mp_int *b) +{ + ARGCHK(a != NULL && b != NULL, MP_EQ); + + if(SIGN(a) == SIGN(b)) { + int mag; + + if((mag = s_mp_cmp(a, b)) == MP_EQ) + return MP_EQ; + + if(SIGN(a) == MP_ZPOS) + return mag; + else + return -mag; + + } else if(SIGN(a) == MP_ZPOS) { + return MP_GT; + } else { + return MP_LT; + } + +} /* end mp_cmp() */ + +/* }}} */ + +/* {{{ mp_cmp_mag(a, b) */ + +/* + mp_cmp_mag(a, b) + + Compares |a| <=> |b|, and returns an appropriate comparison result + */ + +int mp_cmp_mag(mp_int *a, mp_int *b) +{ + ARGCHK(a != NULL && b != NULL, MP_EQ); + + return s_mp_cmp(a, b); + +} /* end mp_cmp_mag() */ + +/* }}} */ + +/* {{{ mp_cmp_int(a, z) */ + +/* + This just converts z to an mp_int, and uses the existing comparison + routines. This is sort of inefficient, but it's not clear to me how + frequently this wil get used anyway. For small positive constants, + you can always use mp_cmp_d(), and for zero, there is mp_cmp_z(). + */ +int mp_cmp_int(mp_int *a, long z) +{ + mp_int tmp; + int out; + + ARGCHK(a != NULL, MP_EQ); + + mp_init(&tmp); mp_set_int(&tmp, z); + out = mp_cmp(a, &tmp); + mp_clear(&tmp); + + return out; + +} /* end mp_cmp_int() */ + +/* }}} */ + +/* {{{ mp_isodd(a) */ + +/* + mp_isodd(a) + + Returns a true (non-zero) value if a is odd, false (zero) otherwise. + */ +int mp_isodd(mp_int *a) +{ + ARGCHK(a != NULL, 0); + + return (DIGIT(a, 0) & 1); + +} /* end mp_isodd() */ + +/* }}} */ + +/* {{{ mp_iseven(a) */ + +int mp_iseven(mp_int *a) +{ + return !mp_isodd(a); + +} /* end mp_iseven() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Number theoretic functions */ + +#if MP_NUMTH +/* {{{ mp_gcd(a, b, c) */ + +/* + Like the old mp_gcd() function, except computes the GCD using the + binary algorithm due to Josef Stein in 1961 (via Knuth). + */ +mp_err mp_gcd(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + mp_int u, v, t; + mp_size k = 0; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(a) == MP_EQ && mp_cmp_z(b) == MP_EQ) + return MP_RANGE; + if(mp_cmp_z(a) == MP_EQ) { + return mp_copy(b, c); + } else if(mp_cmp_z(b) == MP_EQ) { + return mp_copy(a, c); + } + + if((res = mp_init(&t)) != MP_OKAY) + return res; + if((res = mp_init_copy(&u, a)) != MP_OKAY) + goto U; + if((res = mp_init_copy(&v, b)) != MP_OKAY) + goto V; + + SIGN(&u) = MP_ZPOS; + SIGN(&v) = MP_ZPOS; + + /* Divide out common factors of 2 until at least 1 of a, b is even */ + while(mp_iseven(&u) && mp_iseven(&v)) { + s_mp_div_2(&u); + s_mp_div_2(&v); + ++k; + } + + /* Initialize t */ + if(mp_isodd(&u)) { + if((res = mp_copy(&v, &t)) != MP_OKAY) + goto CLEANUP; + + /* t = -v */ + if(SIGN(&v) == MP_ZPOS) + SIGN(&t) = MP_NEG; + else + SIGN(&t) = MP_ZPOS; + + } else { + if((res = mp_copy(&u, &t)) != MP_OKAY) + goto CLEANUP; + + } + + for(;;) { + while(mp_iseven(&t)) { + s_mp_div_2(&t); + } + + if(mp_cmp_z(&t) == MP_GT) { + if((res = mp_copy(&t, &u)) != MP_OKAY) + goto CLEANUP; + + } else { + if((res = mp_copy(&t, &v)) != MP_OKAY) + goto CLEANUP; + + /* v = -t */ + if(SIGN(&t) == MP_ZPOS) + SIGN(&v) = MP_NEG; + else + SIGN(&v) = MP_ZPOS; + } + + if((res = mp_sub(&u, &v, &t)) != MP_OKAY) + goto CLEANUP; + + if(s_mp_cmp_d(&t, 0) == MP_EQ) + break; + } + + s_mp_2expt(&v, k); /* v = 2^k */ + res = mp_mul(&u, &v, c); /* c = u * v */ + + CLEANUP: + mp_clear(&v); + V: + mp_clear(&u); + U: + mp_clear(&t); + + return res; + +} /* end mp_bgcd() */ + +/* }}} */ + +/* {{{ mp_lcm(a, b, c) */ + +/* We compute the least common multiple using the rule: + + ab = [a, b](a, b) + + ... by computing the product, and dividing out the gcd. + */ + +mp_err mp_lcm(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int gcd, prod; + mp_err res; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + /* Set up temporaries */ + if((res = mp_init(&gcd)) != MP_OKAY) + return res; + if((res = mp_init(&prod)) != MP_OKAY) + goto GCD; + + if((res = mp_mul(a, b, &prod)) != MP_OKAY) + goto CLEANUP; + if((res = mp_gcd(a, b, &gcd)) != MP_OKAY) + goto CLEANUP; + + res = mp_div(&prod, &gcd, c, NULL); + + CLEANUP: + mp_clear(&prod); + GCD: + mp_clear(&gcd); + + return res; + +} /* end mp_lcm() */ + +/* }}} */ + +/* {{{ mp_xgcd(a, b, g, x, y) */ + +/* + mp_xgcd(a, b, g, x, y) + + Compute g = (a, b) and values x and y satisfying Bezout's identity + (that is, ax + by = g). This uses the extended binary GCD algorithm + based on the Stein algorithm used for mp_gcd() + */ + +mp_err mp_xgcd(mp_int *a, mp_int *b, mp_int *g, mp_int *x, mp_int *y) +{ + mp_int gx, xc, yc, u, v, A, B, C, D; + mp_int *clean[9]; + mp_err res; + int last = -1; + + if(mp_cmp_z(b) == 0) + return MP_RANGE; + + /* Initialize all these variables we need */ + if((res = mp_init(&u)) != MP_OKAY) goto CLEANUP; + clean[++last] = &u; + if((res = mp_init(&v)) != MP_OKAY) goto CLEANUP; + clean[++last] = &v; + if((res = mp_init(&gx)) != MP_OKAY) goto CLEANUP; + clean[++last] = &gx; + if((res = mp_init(&A)) != MP_OKAY) goto CLEANUP; + clean[++last] = &A; + if((res = mp_init(&B)) != MP_OKAY) goto CLEANUP; + clean[++last] = &B; + if((res = mp_init(&C)) != MP_OKAY) goto CLEANUP; + clean[++last] = &C; + if((res = mp_init(&D)) != MP_OKAY) goto CLEANUP; + clean[++last] = &D; + if((res = mp_init_copy(&xc, a)) != MP_OKAY) goto CLEANUP; + clean[++last] = &xc; + mp_abs(&xc, &xc); + if((res = mp_init_copy(&yc, b)) != MP_OKAY) goto CLEANUP; + clean[++last] = &yc; + mp_abs(&yc, &yc); + + mp_set(&gx, 1); + + /* Divide by two until at least one of them is even */ + while(mp_iseven(&xc) && mp_iseven(&yc)) { + s_mp_div_2(&xc); + s_mp_div_2(&yc); + if((res = s_mp_mul_2(&gx)) != MP_OKAY) + goto CLEANUP; + } + + mp_copy(&xc, &u); + mp_copy(&yc, &v); + mp_set(&A, 1); mp_set(&D, 1); + + /* Loop through binary GCD algorithm */ + for(;;) { + while(mp_iseven(&u)) { + s_mp_div_2(&u); + + if(mp_iseven(&A) && mp_iseven(&B)) { + s_mp_div_2(&A); s_mp_div_2(&B); + } else { + if((res = mp_add(&A, &yc, &A)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&A); + if((res = mp_sub(&B, &xc, &B)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&B); + } + } + + while(mp_iseven(&v)) { + s_mp_div_2(&v); + + if(mp_iseven(&C) && mp_iseven(&D)) { + s_mp_div_2(&C); s_mp_div_2(&D); + } else { + if((res = mp_add(&C, &yc, &C)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&C); + if((res = mp_sub(&D, &xc, &D)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&D); + } + } + + if(mp_cmp(&u, &v) >= 0) { + if((res = mp_sub(&u, &v, &u)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&A, &C, &A)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&B, &D, &B)) != MP_OKAY) goto CLEANUP; + + } else { + if((res = mp_sub(&v, &u, &v)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&C, &A, &C)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&D, &B, &D)) != MP_OKAY) goto CLEANUP; + + } + + /* If we're done, copy results to output */ + if(mp_cmp_z(&u) == 0) { + if(x) + if((res = mp_copy(&C, x)) != MP_OKAY) goto CLEANUP; + + if(y) + if((res = mp_copy(&D, y)) != MP_OKAY) goto CLEANUP; + + if(g) + if((res = mp_mul(&gx, &v, g)) != MP_OKAY) goto CLEANUP; + + break; + } + } + + CLEANUP: + while(last >= 0) + mp_clear(clean[last--]); + + return res; + +} /* end mp_xgcd() */ + +/* }}} */ + +/* {{{ mp_invmod(a, m, c) */ + +/* + mp_invmod(a, m, c) + + Compute c = a^-1 (mod m), if there is an inverse for a (mod m). + This is equivalent to the question of whether (a, m) = 1. If not, + MP_UNDEF is returned, and there is no inverse. + */ + +mp_err mp_invmod(mp_int *a, mp_int *m, mp_int *c) +{ + mp_int g, x; + mp_err res; + + ARGCHK(a && m && c, MP_BADARG); + + if(mp_cmp_z(a) == 0 || mp_cmp_z(m) == 0) + return MP_RANGE; + + if((res = mp_init(&g)) != MP_OKAY) + return res; + if((res = mp_init(&x)) != MP_OKAY) + goto X; + + if((res = mp_xgcd(a, m, &g, &x, NULL)) != MP_OKAY) + goto CLEANUP; + + if(mp_cmp_d(&g, 1) != MP_EQ) { + res = MP_UNDEF; + goto CLEANUP; + } + + res = mp_mod(&x, m, c); + SIGN(c) = SIGN(a); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&g); + + return res; + +} /* end mp_invmod() */ + +/* }}} */ +#endif /* if MP_NUMTH */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ mp_print(mp, ofp) */ + +#if MP_IOFUNC +/* + mp_print(mp, ofp) + + Print a textual representation of the given mp_int on the output + stream 'ofp'. Output is generated using the internal radix. + */ + +void mp_print(mp_int *mp, FILE *ofp) +{ + int ix; + + if(mp == NULL || ofp == NULL) + return; + + fputc((SIGN(mp) == MP_NEG) ? '-' : '+', ofp); + + for(ix = USED(mp) - 1; ix >= 0; ix--) { + fprintf(ofp, DIGIT_FMT, DIGIT(mp, ix)); + } + +} /* end mp_print() */ + +#endif /* if MP_IOFUNC */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ More I/O Functions */ + +/* {{{ mp_read_signed_bin(mp, str, len) */ + +/* + mp_read_signed_bin(mp, str, len) + + Read in a raw value (base 256) into the given mp_int + */ + +mp_err mp_read_signed_bin(mp_int *mp, unsigned char *str, int len) +{ + mp_err res; + + ARGCHK(mp != NULL && str != NULL && len > 0, MP_BADARG); + + if((res = mp_read_unsigned_bin(mp, str + 1, len - 1)) == MP_OKAY) { + /* Get sign from first byte */ + if(str[0]) + SIGN(mp) = MP_NEG; + else + SIGN(mp) = MP_ZPOS; + } + + return res; + +} /* end mp_read_signed_bin() */ + +/* }}} */ + +/* {{{ mp_signed_bin_size(mp) */ + +int mp_signed_bin_size(mp_int *mp) +{ + ARGCHK(mp != NULL, 0); + + return mp_unsigned_bin_size(mp) + 1; + +} /* end mp_signed_bin_size() */ + +/* }}} */ + +/* {{{ mp_to_signed_bin(mp, str) */ + +mp_err mp_to_signed_bin(mp_int *mp, unsigned char *str) +{ + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + + /* Caller responsible for allocating enough memory (use mp_raw_size(mp)) */ + str[0] = (char)SIGN(mp); + + return mp_to_unsigned_bin(mp, str + 1); + +} /* end mp_to_signed_bin() */ + +/* }}} */ + +/* {{{ mp_read_unsigned_bin(mp, str, len) */ + +/* + mp_read_unsigned_bin(mp, str, len) + + Read in an unsigned value (base 256) into the given mp_int + */ + +mp_err mp_read_unsigned_bin(mp_int *mp, unsigned char *str, int len) +{ + int ix; + mp_err res; + + ARGCHK(mp != NULL && str != NULL && len > 0, MP_BADARG); + + mp_zero(mp); + + for(ix = 0; ix < len; ix++) { + if((res = s_mp_mul_2d(mp, CHAR_BIT)) != MP_OKAY) + return res; + + if((res = mp_add_d(mp, str[ix], mp)) != MP_OKAY) + return res; + } + + return MP_OKAY; + +} /* end mp_read_unsigned_bin() */ + +/* }}} */ + +/* {{{ mp_unsigned_bin_size(mp) */ + +int mp_unsigned_bin_size(mp_int *mp) +{ + mp_digit topdig; + int count; + + ARGCHK(mp != NULL, 0); + + /* Special case for the value zero */ + if(USED(mp) == 1 && DIGIT(mp, 0) == 0) + return 1; + + count = (USED(mp) - 1) * sizeof(mp_digit); + topdig = DIGIT(mp, USED(mp) - 1); + + while(topdig != 0) { + ++count; + topdig >>= CHAR_BIT; + } + + return count; + +} /* end mp_unsigned_bin_size() */ + +/* }}} */ + +/* {{{ mp_to_unsigned_bin(mp, str) */ + +mp_err mp_to_unsigned_bin(mp_int *mp, unsigned char *str) +{ + mp_digit *dp, *end, d; + unsigned char *spos; + + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + + dp = DIGITS(mp); + end = dp + USED(mp) - 1; + spos = str; + + /* Special case for zero, quick test */ + if(dp == end && *dp == 0) { + *str = '\0'; + return MP_OKAY; + } + + /* Generate digits in reverse order */ + while(dp < end) { + int ix; + + d = *dp; + for(ix = 0; ix < sizeof(mp_digit); ++ix) { + *spos = d & UCHAR_MAX; + d >>= CHAR_BIT; + ++spos; + } + + ++dp; + } + + /* Now handle last digit specially, high order zeroes are not written */ + d = *end; + while(d != 0) { + *spos = d & UCHAR_MAX; + d >>= CHAR_BIT; + ++spos; + } + + /* Reverse everything to get digits in the correct order */ + while(--spos > str) { + unsigned char t = *str; + *str = *spos; + *spos = t; + + ++str; + } + + return MP_OKAY; + +} /* end mp_to_unsigned_bin() */ + +/* }}} */ + +/* {{{ mp_count_bits(mp) */ + +int mp_count_bits(mp_int *mp) +{ + int len; + mp_digit d; + + ARGCHK(mp != NULL, MP_BADARG); + + len = DIGIT_BIT * (USED(mp) - 1); + d = DIGIT(mp, USED(mp) - 1); + + while(d != 0) { + ++len; + d >>= 1; + } + + return len; + +} /* end mp_count_bits() */ + +/* }}} */ + +/* {{{ mp_read_radix(mp, str, radix) */ + +/* + mp_read_radix(mp, str, radix) + + Read an integer from the given string, and set mp to the resulting + value. The input is presumed to be in base 10. Leading non-digit + characters are ignored, and the function reads until a non-digit + character or the end of the string. + */ + +mp_err mp_read_radix(mp_int *mp, unsigned char *str, int radix) +{ + int ix = 0, val = 0; + mp_err res; + mp_sign sig = MP_ZPOS; + + ARGCHK(mp != NULL && str != NULL && radix >= 2 && radix <= MAX_RADIX, + MP_BADARG); + + mp_zero(mp); + + /* Skip leading non-digit characters until a digit or '-' or '+' */ + while(str[ix] && + (s_mp_tovalue(str[ix], radix) < 0) && + str[ix] != '-' && + str[ix] != '+') { + ++ix; + } + + if(str[ix] == '-') { + sig = MP_NEG; + ++ix; + } else if(str[ix] == '+') { + sig = MP_ZPOS; /* this is the default anyway... */ + ++ix; + } + + while((val = s_mp_tovalue(str[ix], radix)) >= 0) { + if((res = s_mp_mul_d(mp, radix)) != MP_OKAY) + return res; + if((res = s_mp_add_d(mp, val)) != MP_OKAY) + return res; + ++ix; + } + + if(s_mp_cmp_d(mp, 0) == MP_EQ) + SIGN(mp) = MP_ZPOS; + else + SIGN(mp) = sig; + + return MP_OKAY; + +} /* end mp_read_radix() */ + +/* }}} */ + +/* {{{ mp_radix_size(mp, radix) */ + +int mp_radix_size(mp_int *mp, int radix) +{ + int len; + ARGCHK(mp != NULL, 0); + + len = s_mp_outlen(mp_count_bits(mp), radix) + 1; /* for NUL terminator */ + + if(mp_cmp_z(mp) < 0) + ++len; /* for sign */ + + return len; + +} /* end mp_radix_size() */ + +/* }}} */ + +/* {{{ mp_value_radix_size(num, qty, radix) */ + +/* num = number of digits + qty = number of bits per digit + radix = target base + + Return the number of digits in the specified radix that would be + needed to express 'num' digits of 'qty' bits each. + */ +int mp_value_radix_size(int num, int qty, int radix) +{ + ARGCHK(num >= 0 && qty > 0 && radix >= 2 && radix <= MAX_RADIX, 0); + + return s_mp_outlen(num * qty, radix); + +} /* end mp_value_radix_size() */ + +/* }}} */ + +/* {{{ mp_toradix(mp, str, radix) */ + +mp_err mp_toradix(mp_int *mp, unsigned char *str, int radix) +{ + int ix, pos = 0; + + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + ARGCHK(radix > 1 && radix <= MAX_RADIX, MP_RANGE); + + if(mp_cmp_z(mp) == MP_EQ) { + str[0] = '0'; + str[1] = '\0'; + } else { + mp_err res; + mp_int tmp; + mp_sign sgn; + mp_digit rem, rdx = (mp_digit)radix; + char ch; + + if((res = mp_init_copy(&tmp, mp)) != MP_OKAY) + return res; + + /* Save sign for later, and take absolute value */ + sgn = SIGN(&tmp); SIGN(&tmp) = MP_ZPOS; + + /* Generate output digits in reverse order */ + while(mp_cmp_z(&tmp) != 0) { + if((res = s_mp_div_d(&tmp, rdx, &rem)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + /* Generate digits, use capital letters */ + ch = s_mp_todigit(rem, radix, 0); + + str[pos++] = ch; + } + + /* Add - sign if original value was negative */ + if(sgn == MP_NEG) + str[pos++] = '-'; + + /* Add trailing NUL to end the string */ + str[pos--] = '\0'; + + /* Reverse the digits and sign indicator */ + ix = 0; + while(ix < pos) { + char tmp = str[ix]; + + str[ix] = str[pos]; + str[pos] = tmp; + ++ix; + --pos; + } + + mp_clear(&tmp); + } + + return MP_OKAY; + +} /* end mp_toradix() */ + +/* }}} */ + +/* {{{ mp_char2value(ch, r) */ + +int mp_char2value(char ch, int r) +{ + return s_mp_tovalue(ch, r); + +} /* end mp_tovalue() */ + +/* }}} */ + +/* }}} */ + +/* {{{ mp_strerror(ec) */ + +/* + mp_strerror(ec) + + Return a string describing the meaning of error code 'ec'. The + string returned is allocated in static memory, so the caller should + not attempt to modify or free the memory associated with this + string. + */ +const char *mp_strerror(mp_err ec) +{ + int aec = (ec < 0) ? -ec : ec; + + /* Code values are negative, so the senses of these comparisons + are accurate */ + if(ec < MP_LAST_CODE || ec > MP_OKAY) { + return mp_err_string[0]; /* unknown error code */ + } else { + return mp_err_string[aec + 1]; + } + +} /* end mp_strerror() */ + +/* }}} */ + +/*========================================================================*/ +/*------------------------------------------------------------------------*/ +/* Static function definitions (internal use only) */ + +/* {{{ Memory management */ + +/* {{{ s_mp_grow(mp, min) */ + +/* Make sure there are at least 'min' digits allocated to mp */ +mp_err s_mp_grow(mp_int *mp, mp_size min) +{ + if(min > ALLOC(mp)) { + mp_digit *tmp; + + /* Set min to next nearest default precision block size */ + min = ((min + (s_mp_defprec - 1)) / s_mp_defprec) * s_mp_defprec; + + if((tmp = s_mp_alloc(min, sizeof(mp_digit))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(mp), tmp, USED(mp)); + +#if MP_CRYPTO + s_mp_setz(DIGITS(mp), ALLOC(mp)); +#endif + s_mp_free(DIGITS(mp)); + DIGITS(mp) = tmp; + ALLOC(mp) = min; + } + + return MP_OKAY; + +} /* end s_mp_grow() */ + +/* }}} */ + +/* {{{ s_mp_pad(mp, min) */ + +/* Make sure the used size of mp is at least 'min', growing if needed */ +mp_err s_mp_pad(mp_int *mp, mp_size min) +{ + if(min > USED(mp)) { + mp_err res; + + /* Make sure there is room to increase precision */ + if(min > ALLOC(mp) && (res = s_mp_grow(mp, min)) != MP_OKAY) + return res; + + /* Increase precision; should already be 0-filled */ + USED(mp) = min; + } + + return MP_OKAY; + +} /* end s_mp_pad() */ + +/* }}} */ + +/* {{{ s_mp_setz(dp, count) */ + +#if MP_MACRO == 0 +/* Set 'count' digits pointed to by dp to be zeroes */ +void s_mp_setz(mp_digit *dp, mp_size count) +{ +#if MP_MEMSET == 0 + int ix; + + for(ix = 0; ix < count; ix++) + dp[ix] = 0; +#else + memset(dp, 0, count * sizeof(mp_digit)); +#endif + +} /* end s_mp_setz() */ +#endif + +/* }}} */ + +/* {{{ s_mp_copy(sp, dp, count) */ + +#if MP_MACRO == 0 +/* Copy 'count' digits from sp to dp */ +void s_mp_copy(mp_digit *sp, mp_digit *dp, mp_size count) +{ +#if MP_MEMCPY == 0 + int ix; + + for(ix = 0; ix < count; ix++) + dp[ix] = sp[ix]; +#else + memcpy(dp, sp, count * sizeof(mp_digit)); +#endif + +} /* end s_mp_copy() */ +#endif + +/* }}} */ + +/* {{{ s_mp_alloc(nb, ni) */ + +#if MP_MACRO == 0 +/* Allocate ni records of nb bytes each, and return a pointer to that */ +void *s_mp_alloc(size_t nb, size_t ni) +{ + return calloc(nb, ni); + +} /* end s_mp_alloc() */ +#endif + +/* }}} */ + +/* {{{ s_mp_free(ptr) */ + +#if MP_MACRO == 0 +/* Free the memory pointed to by ptr */ +void s_mp_free(void *ptr) +{ + if(ptr) + free(ptr); + +} /* end s_mp_free() */ +#endif + +/* }}} */ + +/* {{{ s_mp_clamp(mp) */ + +/* Remove leading zeroes from the given value */ +void s_mp_clamp(mp_int *mp) +{ + mp_size du = USED(mp); + mp_digit *zp = DIGITS(mp) + du - 1; + + while(du > 1 && !*zp--) + --du; + + USED(mp) = du; + +} /* end s_mp_clamp() */ + + +/* }}} */ + +/* {{{ s_mp_exch(a, b) */ + +/* Exchange the data for a and b; (b, a) = (a, b) */ +void s_mp_exch(mp_int *a, mp_int *b) +{ + mp_int tmp; + + tmp = *a; + *a = *b; + *b = tmp; + +} /* end s_mp_exch() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Arithmetic helpers */ + +/* {{{ s_mp_lshd(mp, p) */ + +/* + Shift mp leftward by p digits, growing if needed, and zero-filling + the in-shifted digits at the right end. This is a convenient + alternative to multiplication by powers of the radix + */ + +mp_err s_mp_lshd(mp_int *mp, mp_size p) +{ + mp_err res; + mp_size pos; + mp_digit *dp; + int ix; + + if(p == 0) + return MP_OKAY; + + if((res = s_mp_pad(mp, USED(mp) + p)) != MP_OKAY) + return res; + + pos = USED(mp) - 1; + dp = DIGITS(mp); + + /* Shift all the significant figures over as needed */ + for(ix = pos - p; ix >= 0; ix--) + dp[ix + p] = dp[ix]; + + /* Fill the bottom digits with zeroes */ + for(ix = 0; ix < p; ix++) + dp[ix] = 0; + + return MP_OKAY; + +} /* end s_mp_lshd() */ + +/* }}} */ + +/* {{{ s_mp_rshd(mp, p) */ + +/* + Shift mp rightward by p digits. Maintains the invariant that + digits above the precision are all zero. Digits shifted off the + end are lost. Cannot fail. + */ + +void s_mp_rshd(mp_int *mp, mp_size p) +{ + mp_size ix; + mp_digit *dp; + + if(p == 0) + return; + + /* Shortcut when all digits are to be shifted off */ + if(p >= USED(mp)) { + s_mp_setz(DIGITS(mp), ALLOC(mp)); + USED(mp) = 1; + SIGN(mp) = MP_ZPOS; + return; + } + + /* Shift all the significant figures over as needed */ + dp = DIGITS(mp); + for(ix = p; ix < USED(mp); ix++) + dp[ix - p] = dp[ix]; + + /* Fill the top digits with zeroes */ + ix -= p; + while(ix < USED(mp)) + dp[ix++] = 0; + + /* Strip off any leading zeroes */ + s_mp_clamp(mp); + +} /* end s_mp_rshd() */ + +/* }}} */ + +/* {{{ s_mp_div_2(mp) */ + +/* Divide by two -- take advantage of radix properties to do it fast */ +void s_mp_div_2(mp_int *mp) +{ + s_mp_div_2d(mp, 1); + +} /* end s_mp_div_2() */ + +/* }}} */ + +/* {{{ s_mp_mul_2(mp) */ + +mp_err s_mp_mul_2(mp_int *mp) +{ + int ix; + mp_digit kin = 0, kout, *dp = DIGITS(mp); + mp_err res; + + /* Shift digits leftward by 1 bit */ + for(ix = 0; ix < USED(mp); ix++) { + kout = (dp[ix] >> (DIGIT_BIT - 1)) & 1; + dp[ix] = (dp[ix] << 1) | kin; + + kin = kout; + } + + /* Deal with rollover from last digit */ + if(kin) { + if(ix >= ALLOC(mp)) { + if((res = s_mp_grow(mp, ALLOC(mp) + 1)) != MP_OKAY) + return res; + dp = DIGITS(mp); + } + + dp[ix] = kin; + USED(mp) += 1; + } + + return MP_OKAY; + +} /* end s_mp_mul_2() */ + +/* }}} */ + +/* {{{ s_mp_mod_2d(mp, d) */ + +/* + Remainder the integer by 2^d, where d is a number of bits. This + amounts to a bitwise AND of the value, and does not require the full + division code + */ +void s_mp_mod_2d(mp_int *mp, mp_digit d) +{ + unsigned int ndig = (d / DIGIT_BIT), nbit = (d % DIGIT_BIT); + unsigned int ix; + mp_digit dmask, *dp = DIGITS(mp); + + if(ndig >= USED(mp)) + return; + + /* Flush all the bits above 2^d in its digit */ + dmask = (1 << nbit) - 1; + dp[ndig] &= dmask; + + /* Flush all digits above the one with 2^d in it */ + for(ix = ndig + 1; ix < USED(mp); ix++) + dp[ix] = 0; + + s_mp_clamp(mp); + +} /* end s_mp_mod_2d() */ + +/* }}} */ + +/* {{{ s_mp_mul_2d(mp, d) */ + +/* + Multiply by the integer 2^d, where d is a number of bits. This + amounts to a bitwise shift of the value, and does not require the + full multiplication code. + */ +mp_err s_mp_mul_2d(mp_int *mp, mp_digit d) +{ + mp_err res; + mp_digit save, next, mask, *dp; + mp_size used; + int ix; + + if((res = s_mp_lshd(mp, d / DIGIT_BIT)) != MP_OKAY) + return res; + + dp = DIGITS(mp); used = USED(mp); + d %= DIGIT_BIT; + + mask = (1 << d) - 1; + + /* If the shift requires another digit, make sure we've got one to + work with */ + if((dp[used - 1] >> (DIGIT_BIT - d)) & mask) { + if((res = s_mp_grow(mp, used + 1)) != MP_OKAY) + return res; + dp = DIGITS(mp); + } + + /* Do the shifting... */ + save = 0; + for(ix = 0; ix < used; ix++) { + next = (dp[ix] >> (DIGIT_BIT - d)) & mask; + dp[ix] = (dp[ix] << d) | save; + save = next; + } + + /* If, at this point, we have a nonzero carryout into the next + digit, we'll increase the size by one digit, and store it... + */ + if(save) { + dp[used] = save; + USED(mp) += 1; + } + + s_mp_clamp(mp); + return MP_OKAY; + +} /* end s_mp_mul_2d() */ + +/* }}} */ + +/* {{{ s_mp_div_2d(mp, d) */ + +/* + Divide the integer by 2^d, where d is a number of bits. This + amounts to a bitwise shift of the value, and does not require the + full division code (used in Barrett reduction, see below) + */ +void s_mp_div_2d(mp_int *mp, mp_digit d) +{ + int ix; + mp_digit save, next, mask, *dp = DIGITS(mp); + + s_mp_rshd(mp, d / DIGIT_BIT); + d %= DIGIT_BIT; + + mask = (1 << d) - 1; + + save = 0; + for(ix = USED(mp) - 1; ix >= 0; ix--) { + next = dp[ix] & mask; + dp[ix] = (dp[ix] >> d) | (save << (DIGIT_BIT - d)); + save = next; + } + + s_mp_clamp(mp); + +} /* end s_mp_div_2d() */ + +/* }}} */ + +/* {{{ s_mp_norm(a, b) */ + +/* + s_mp_norm(a, b) + + Normalize a and b for division, where b is the divisor. In order + that we might make good guesses for quotient digits, we want the + leading digit of b to be at least half the radix, which we + accomplish by multiplying a and b by a constant. This constant is + returned (so that it can be divided back out of the remainder at the + end of the division process). + + We multiply by the smallest power of 2 that gives us a leading digit + at least half the radix. By choosing a power of 2, we simplify the + multiplication and division steps to simple shifts. + */ +mp_digit s_mp_norm(mp_int *a, mp_int *b) +{ + mp_digit t, d = 0; + + t = DIGIT(b, USED(b) - 1); + while(t < (RADIX / 2)) { + t <<= 1; + ++d; + } + + if(d != 0) { + s_mp_mul_2d(a, d); + s_mp_mul_2d(b, d); + } + + return d; + +} /* end s_mp_norm() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive digit arithmetic */ + +/* {{{ s_mp_add_d(mp, d) */ + +/* Add d to |mp| in place */ +mp_err s_mp_add_d(mp_int *mp, mp_digit d) /* unsigned digit addition */ +{ + mp_word w, k = 0; + mp_size ix = 1, used = USED(mp); + mp_digit *dp = DIGITS(mp); + + w = dp[0] + d; + dp[0] = ACCUM(w); + k = CARRYOUT(w); + + while(ix < used && k) { + w = dp[ix] + k; + dp[ix] = ACCUM(w); + k = CARRYOUT(w); + ++ix; + } + + if(k != 0) { + mp_err res; + + if((res = s_mp_pad(mp, USED(mp) + 1)) != MP_OKAY) + return res; + + DIGIT(mp, ix) = k; + } + + return MP_OKAY; + +} /* end s_mp_add_d() */ + +/* }}} */ + +/* {{{ s_mp_sub_d(mp, d) */ + +/* Subtract d from |mp| in place, assumes |mp| > d */ +mp_err s_mp_sub_d(mp_int *mp, mp_digit d) /* unsigned digit subtract */ +{ + mp_word w, b = 0; + mp_size ix = 1, used = USED(mp); + mp_digit *dp = DIGITS(mp); + + /* Compute initial subtraction */ + w = (RADIX + dp[0]) - d; + b = CARRYOUT(w) ? 0 : 1; + dp[0] = ACCUM(w); + + /* Propagate borrows leftward */ + while(b && ix < used) { + w = (RADIX + dp[ix]) - b; + b = CARRYOUT(w) ? 0 : 1; + dp[ix] = ACCUM(w); + ++ix; + } + + /* Remove leading zeroes */ + s_mp_clamp(mp); + + /* If we have a borrow out, it's a violation of the input invariant */ + if(b) + return MP_RANGE; + else + return MP_OKAY; + +} /* end s_mp_sub_d() */ + +/* }}} */ + +/* {{{ s_mp_mul_d(a, d) */ + +/* Compute a = a * d, single digit multiplication */ +mp_err s_mp_mul_d(mp_int *a, mp_digit d) +{ + mp_word w, k = 0; + mp_size ix, max; + mp_err res; + mp_digit *dp = DIGITS(a); + + /* + Single-digit multiplication will increase the precision of the + output by at most one digit. However, we can detect when this + will happen -- if the high-order digit of a, times d, gives a + two-digit result, then the precision of the result will increase; + otherwise it won't. We use this fact to avoid calling s_mp_pad() + unless absolutely necessary. + */ + max = USED(a); + w = dp[max - 1] * d; + if(CARRYOUT(w) != 0) { + if((res = s_mp_pad(a, max + 1)) != MP_OKAY) + return res; + dp = DIGITS(a); + } + + for(ix = 0; ix < max; ix++) { + w = (dp[ix] * d) + k; + dp[ix] = ACCUM(w); + k = CARRYOUT(w); + } + + /* If there is a precision increase, take care of it here; the above + test guarantees we have enough storage to do this safely. + */ + if(k) { + dp[max] = k; + USED(a) = max + 1; + } + + s_mp_clamp(a); + + return MP_OKAY; + +} /* end s_mp_mul_d() */ + +/* }}} */ + +/* {{{ s_mp_div_d(mp, d, r) */ + +/* + s_mp_div_d(mp, d, r) + + Compute the quotient mp = mp / d and remainder r = mp mod d, for a + single digit d. If r is null, the remainder will be discarded. + */ + +mp_err s_mp_div_d(mp_int *mp, mp_digit d, mp_digit *r) +{ + mp_word w = 0, t; + mp_int quot; + mp_err res; + mp_digit *dp = DIGITS(mp), *qp; + int ix; + + if(d == 0) + return MP_RANGE; + + /* Make room for the quotient */ + if((res = mp_init_size(", USED(mp))) != MP_OKAY) + return res; + + USED(") = USED(mp); /* so clamping will work below */ + qp = DIGITS("); + + /* Divide without subtraction */ + for(ix = USED(mp) - 1; ix >= 0; ix--) { + w = (w << DIGIT_BIT) | dp[ix]; + + if(w >= d) { + t = w / d; + w = w % d; + } else { + t = 0; + } + + qp[ix] = t; + } + + /* Deliver the remainder, if desired */ + if(r) + *r = w; + + s_mp_clamp("); + mp_exch(", mp); + mp_clear("); + + return MP_OKAY; + +} /* end s_mp_div_d() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive full arithmetic */ + +/* {{{ s_mp_add(a, b) */ + +/* Compute a = |a| + |b| */ +mp_err s_mp_add(mp_int *a, mp_int *b) /* magnitude addition */ +{ + mp_word w = 0; + mp_digit *pa, *pb; + mp_size ix, used = USED(b); + mp_err res; + + /* Make sure a has enough precision for the output value */ + if((used > USED(a)) && (res = s_mp_pad(a, used)) != MP_OKAY) + return res; + + /* + Add up all digits up to the precision of b. If b had initially + the same precision as a, or greater, we took care of it by the + padding step above, so there is no problem. If b had initially + less precision, we'll have to make sure the carry out is duly + propagated upward among the higher-order digits of the sum. + */ + pa = DIGITS(a); + pb = DIGITS(b); + for(ix = 0; ix < used; ++ix) { + w += *pa + *pb++; + *pa++ = ACCUM(w); + w = CARRYOUT(w); + } + + /* If we run out of 'b' digits before we're actually done, make + sure the carries get propagated upward... + */ + used = USED(a); + while(w && ix < used) { + w += *pa; + *pa++ = ACCUM(w); + w = CARRYOUT(w); + ++ix; + } + + /* If there's an overall carry out, increase precision and include + it. We could have done this initially, but why touch the memory + allocator unless we're sure we have to? + */ + if(w) { + if((res = s_mp_pad(a, used + 1)) != MP_OKAY) + return res; + + DIGIT(a, ix) = w; /* pa may not be valid after s_mp_pad() call */ + } + + return MP_OKAY; + +} /* end s_mp_add() */ + +/* }}} */ + +/* {{{ s_mp_sub(a, b) */ + +/* Compute a = |a| - |b|, assumes |a| >= |b| */ +mp_err s_mp_sub(mp_int *a, mp_int *b) /* magnitude subtract */ +{ + mp_word w = 0; + mp_digit *pa, *pb; + mp_size ix, used = USED(b); + + /* + Subtract and propagate borrow. Up to the precision of b, this + accounts for the digits of b; after that, we just make sure the + carries get to the right place. This saves having to pad b out to + the precision of a just to make the loops work right... + */ + pa = DIGITS(a); + pb = DIGITS(b); + + for(ix = 0; ix < used; ++ix) { + w = (RADIX + *pa) - w - *pb++; + *pa++ = ACCUM(w); + w = CARRYOUT(w) ? 0 : 1; + } + + used = USED(a); + while(ix < used) { + w = RADIX + *pa - w; + *pa++ = ACCUM(w); + w = CARRYOUT(w) ? 0 : 1; + ++ix; + } + + /* Clobber any leading zeroes we created */ + s_mp_clamp(a); + + /* + If there was a borrow out, then |b| > |a| in violation + of our input invariant. We've already done the work, + but we'll at least complain about it... + */ + if(w) + return MP_RANGE; + else + return MP_OKAY; + +} /* end s_mp_sub() */ + +/* }}} */ + +mp_err s_mp_reduce(mp_int *x, mp_int *m, mp_int *mu) +{ + mp_int q; + mp_err res; + mp_size um = USED(m); + + if((res = mp_init_copy(&q, x)) != MP_OKAY) + return res; + + s_mp_rshd(&q, um - 1); /* q1 = x / b^(k-1) */ + s_mp_mul(&q, mu); /* q2 = q1 * mu */ + s_mp_rshd(&q, um + 1); /* q3 = q2 / b^(k+1) */ + + /* x = x mod b^(k+1), quick (no division) */ + s_mp_mod_2d(x, (mp_digit)(DIGIT_BIT * (um + 1))); + + /* q = q * m mod b^(k+1), quick (no division), uses the short multiplier */ +#ifndef SHRT_MUL + s_mp_mul(&q, m); + s_mp_mod_2d(&q, (mp_digit)(DIGIT_BIT * (um + 1))); +#else + s_mp_mul_dig(&q, m, um + 1); +#endif + + /* 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_z(x) < 0) { + mp_set(&q, 1); + if((res = s_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) >= 0) { + if((res = s_mp_sub(x, m)) != MP_OKAY) + break; + } + + CLEANUP: + mp_clear(&q); + + return res; + +} /* end s_mp_reduce() */ + + + +/* {{{ s_mp_mul(a, b) */ + +/* Compute a = |a| * |b| */ +mp_err s_mp_mul(mp_int *a, mp_int *b) +{ + mp_word w, k = 0; + mp_int tmp; + mp_err res; + mp_size ix, jx, ua = USED(a), ub = USED(b); + mp_digit *pa, *pb, *pt, *pbt; + + if((res = mp_init_size(&tmp, ua + ub)) != MP_OKAY) + return res; + + /* This has the effect of left-padding with zeroes... */ + USED(&tmp) = ua + ub; + + /* We're going to need the base value each iteration */ + pbt = DIGITS(&tmp); + + /* Outer loop: Digits of b */ + + pb = DIGITS(b); + for(ix = 0; ix < ub; ++ix, ++pb) { + if(*pb == 0) + continue; + + /* Inner product: Digits of a */ + pa = DIGITS(a); + for(jx = 0; jx < ua; ++jx, ++pa) { + pt = pbt + ix + jx; + w = *pb * *pa + k + *pt; + *pt = ACCUM(w); + k = CARRYOUT(w); + } + + pbt[ix + jx] = k; + k = 0; + } + + s_mp_clamp(&tmp); + s_mp_exch(&tmp, a); + + mp_clear(&tmp); + + return MP_OKAY; + +} /* end s_mp_mul() */ + +/* }}} */ + +/* {{{ s_mp_kmul(a, b, out, len) */ + +#if 0 +void s_mp_kmul(mp_digit *a, mp_digit *b, mp_digit *out, mp_size len) +{ + mp_word w, k = 0; + mp_size ix, jx; + mp_digit *pa, *pt; + + for(ix = 0; ix < len; ++ix, ++b) { + if(*b == 0) + continue; + + pa = a; + for(jx = 0; jx < len; ++jx, ++pa) { + pt = out + ix + jx; + w = *b * *pa + k + *pt; + *pt = ACCUM(w); + k = CARRYOUT(w); + } + + out[ix + jx] = k; + k = 0; + } + +} /* end s_mp_kmul() */ +#endif + +/* }}} */ + +/* {{{ s_mp_sqr(a) */ + +/* + Computes the square of a, in place. This can be done more + efficiently than a general multiplication, because many of the + computation steps are redundant when squaring. The inner product + step is a bit more complicated, but we save a fair number of + iterations of the multiplication loop. + */ +#if MP_SQUARE +mp_err s_mp_sqr(mp_int *a) +{ + mp_word w, k = 0; + mp_int tmp; + mp_err res; + mp_size ix, jx, kx, used = USED(a); + mp_digit *pa1, *pa2, *pt, *pbt; + + if((res = mp_init_size(&tmp, 2 * used)) != MP_OKAY) + return res; + + /* Left-pad with zeroes */ + USED(&tmp) = 2 * used; + + /* We need the base value each time through the loop */ + pbt = DIGITS(&tmp); + + pa1 = DIGITS(a); + for(ix = 0; ix < used; ++ix, ++pa1) { + if(*pa1 == 0) + continue; + + w = DIGIT(&tmp, ix + ix) + (*pa1 * *pa1); + + pbt[ix + ix] = ACCUM(w); + k = CARRYOUT(w); + + /* + The inner product is computed as: + + (C, S) = t[i,j] + 2 a[i] a[j] + C + + This can overflow what can be represented in an mp_word, and + since C arithmetic does not provide any way to check for + overflow, we have to check explicitly for overflow conditions + before they happen. + */ + for(jx = ix + 1, pa2 = DIGITS(a) + jx; jx < used; ++jx, ++pa2) { + mp_word u = 0, v; + + /* Store this in a temporary to avoid indirections later */ + pt = pbt + ix + jx; + + /* Compute the multiplicative step */ + w = *pa1 * *pa2; + + /* If w is more than half MP_WORD_MAX, the doubling will + overflow, and we need to record a carry out into the next + word */ + u = (w >> (MP_WORD_BIT - 1)) & 1; + + /* Double what we've got, overflow will be ignored as defined + for C arithmetic (we've already noted if it is to occur) + */ + w *= 2; + + /* Compute the additive step */ + v = *pt + k; + + /* If we do not already have an overflow carry, check to see + if the addition will cause one, and set the carry out if so + */ + u |= ((MP_WORD_MAX - v) < w); + + /* Add in the rest, again ignoring overflow */ + w += v; + + /* Set the i,j digit of the output */ + *pt = ACCUM(w); + + /* Save carry information for the next iteration of the loop. + This is why k must be an mp_word, instead of an mp_digit */ + k = CARRYOUT(w) | (u << DIGIT_BIT); + + } /* for(jx ...) */ + + /* Set the last digit in the cycle and reset the carry */ + k = DIGIT(&tmp, ix + jx) + k; + pbt[ix + jx] = ACCUM(k); + k = CARRYOUT(k); + + /* If we are carrying out, propagate the carry to the next digit + in the output. This may cascade, so we have to be somewhat + circumspect -- but we will have enough precision in the output + that we won't overflow + */ + kx = 1; + while(k) { + k = pbt[ix + jx + kx] + 1; + pbt[ix + jx + kx] = ACCUM(k); + k = CARRYOUT(k); + ++kx; + } + } /* for(ix ...) */ + + s_mp_clamp(&tmp); + s_mp_exch(&tmp, a); + + mp_clear(&tmp); + + return MP_OKAY; + +} /* end s_mp_sqr() */ +#endif + +/* }}} */ + +/* {{{ s_mp_div(a, b) */ + +/* + s_mp_div(a, b) + + Compute a = a / b and b = a mod b. Assumes b > a. + */ + +mp_err s_mp_div(mp_int *a, mp_int *b) +{ + mp_int quot, rem, t; + mp_word q; + mp_err res; + mp_digit d; + int ix; + + if(mp_cmp_z(b) == 0) + return MP_RANGE; + + /* Shortcut if b is power of two */ + if((ix = s_mp_ispow2(b)) >= 0) { + mp_copy(a, b); /* need this for remainder */ + s_mp_div_2d(a, (mp_digit)ix); + s_mp_mod_2d(b, (mp_digit)ix); + + return MP_OKAY; + } + + /* Allocate space to store the quotient */ + if((res = mp_init_size(", USED(a))) != MP_OKAY) + return res; + + /* A working temporary for division */ + if((res = mp_init_size(&t, USED(a))) != MP_OKAY) + goto T; + + /* Allocate space for the remainder */ + if((res = mp_init_size(&rem, USED(a))) != MP_OKAY) + goto REM; + + /* Normalize to optimize guessing */ + d = s_mp_norm(a, b); + + /* Perform the division itself...woo! */ + ix = USED(a) - 1; + + while(ix >= 0) { + /* Find a partial substring of a which is at least b */ + while(s_mp_cmp(&rem, b) < 0 && ix >= 0) { + if((res = s_mp_lshd(&rem, 1)) != MP_OKAY) + goto CLEANUP; + + if((res = s_mp_lshd(", 1)) != MP_OKAY) + goto CLEANUP; + + DIGIT(&rem, 0) = DIGIT(a, ix); + s_mp_clamp(&rem); + --ix; + } + + /* If we didn't find one, we're finished dividing */ + if(s_mp_cmp(&rem, b) < 0) + break; + + /* Compute a guess for the next quotient digit */ + q = DIGIT(&rem, USED(&rem) - 1); + if(q <= DIGIT(b, USED(b) - 1) && USED(&rem) > 1) + q = (q << DIGIT_BIT) | DIGIT(&rem, USED(&rem) - 2); + + q /= DIGIT(b, USED(b) - 1); + + /* The guess can be as much as RADIX + 1 */ + if(q >= RADIX) + q = RADIX - 1; + + /* See what that multiplies out to */ + mp_copy(b, &t); + if((res = s_mp_mul_d(&t, q)) != MP_OKAY) + goto CLEANUP; + + /* + If it's too big, back it off. We should not have to do this + more than once, or, in rare cases, twice. Knuth describes a + method by which this could be reduced to a maximum of once, but + I didn't implement that here. + */ + while(s_mp_cmp(&t, &rem) > 0) { + --q; + s_mp_sub(&t, b); + } + + /* At this point, q should be the right next digit */ + if((res = s_mp_sub(&rem, &t)) != MP_OKAY) + goto CLEANUP; + + /* + Include the digit in the quotient. We allocated enough memory + for any quotient we could ever possibly get, so we should not + have to check for failures here + */ + DIGIT(", 0) = q; + } + + /* Denormalize remainder */ + if(d != 0) + s_mp_div_2d(&rem, d); + + s_mp_clamp("); + s_mp_clamp(&rem); + + /* Copy quotient back to output */ + s_mp_exch(", a); + + /* Copy remainder back to output */ + s_mp_exch(&rem, b); + +CLEANUP: + mp_clear(&rem); +REM: + mp_clear(&t); +T: + mp_clear("); + + return res; + +} /* end s_mp_div() */ + +/* }}} */ + +/* {{{ s_mp_2expt(a, k) */ + +mp_err s_mp_2expt(mp_int *a, mp_digit k) +{ + mp_err res; + mp_size dig, bit; + + dig = k / DIGIT_BIT; + bit = k % DIGIT_BIT; + + mp_zero(a); + if((res = s_mp_pad(a, dig + 1)) != MP_OKAY) + return res; + + DIGIT(a, dig) |= (1 << bit); + + return MP_OKAY; + +} /* end s_mp_2expt() */ + +/* }}} */ + + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive comparisons */ + +/* {{{ s_mp_cmp(a, b) */ + +/* Compare |a| <=> |b|, return 0 if equal, <0 if a0 if a>b */ +int s_mp_cmp(mp_int *a, mp_int *b) +{ + mp_size ua = USED(a), ub = USED(b); + + if(ua > ub) + return MP_GT; + else if(ua < ub) + return MP_LT; + else { + int ix = ua - 1; + mp_digit *ap = DIGITS(a) + ix, *bp = DIGITS(b) + ix; + + while(ix >= 0) { + if(*ap > *bp) + return MP_GT; + else if(*ap < *bp) + return MP_LT; + + --ap; --bp; --ix; + } + + return MP_EQ; + } + +} /* end s_mp_cmp() */ + +/* }}} */ + +/* {{{ s_mp_cmp_d(a, d) */ + +/* Compare |a| <=> d, return 0 if equal, <0 if a0 if a>d */ +int s_mp_cmp_d(mp_int *a, mp_digit d) +{ + mp_size ua = USED(a); + mp_digit *ap = DIGITS(a); + + if(ua > 1) + return MP_GT; + + if(*ap < d) + return MP_LT; + else if(*ap > d) + return MP_GT; + else + return MP_EQ; + +} /* end s_mp_cmp_d() */ + +/* }}} */ + +/* {{{ s_mp_ispow2(v) */ + +/* + Returns -1 if the value is not a power of two; otherwise, it returns + k such that v = 2^k, i.e. lg(v). + */ +int s_mp_ispow2(mp_int *v) +{ + mp_digit d, *dp; + mp_size uv = USED(v); + int extra = 0, ix; + + d = DIGIT(v, uv - 1); /* most significant digit of v */ + + while(d && ((d & 1) == 0)) { + d >>= 1; + ++extra; + } + + if(d == 1) { + ix = uv - 2; + dp = DIGITS(v) + ix; + + while(ix >= 0) { + if(*dp) + return -1; /* not a power of two */ + + --dp; --ix; + } + + return ((uv - 1) * DIGIT_BIT) + extra; + } + + return -1; + +} /* end s_mp_ispow2() */ + +/* }}} */ + +/* {{{ s_mp_ispow2d(d) */ + +int s_mp_ispow2d(mp_digit d) +{ + int pow = 0; + + while((d & 1) == 0) { + ++pow; d >>= 1; + } + + if(d == 1) + return pow; + + return -1; + +} /* end s_mp_ispow2d() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive I/O helpers */ + +/* {{{ s_mp_tovalue(ch, r) */ + +/* + Convert the given character to its digit value, in the given radix. + If the given character is not understood in the given radix, -1 is + returned. Otherwise the digit's numeric value is returned. + + The results will be odd if you use a radix < 2 or > 62, you are + expected to know what you're up to. + */ +int s_mp_tovalue(char ch, int r) +{ + int val, xch; + + if(r > 36) + xch = ch; + else + xch = toupper(ch); + + if(isdigit(xch)) + val = xch - '0'; + else if(isupper(xch)) + val = xch - 'A' + 10; + else if(islower(xch)) + val = xch - 'a' + 36; + else if(xch == '+') + val = 62; + else if(xch == '/') + val = 63; + else + return -1; + + if(val < 0 || val >= r) + return -1; + + return val; + +} /* end s_mp_tovalue() */ + +/* }}} */ + +/* {{{ s_mp_todigit(val, r, low) */ + +/* + Convert val to a radix-r digit, if possible. If val is out of range + for r, returns zero. Otherwise, returns an ASCII character denoting + the value in the given radix. + + The results may be odd if you use a radix < 2 or > 64, you are + expected to know what you're doing. + */ + +char s_mp_todigit(int val, int r, int low) +{ + char ch; + + if(val < 0 || val >= r) + return 0; + + ch = s_dmap_1[val]; + + if(r <= 36 && low) + ch = tolower(ch); + + return ch; + +} /* end s_mp_todigit() */ + +/* }}} */ + +/* {{{ s_mp_outlen(bits, radix) */ + +/* + Return an estimate for how long a string is needed to hold a radix + r representation of a number with 'bits' significant bits. + + Does not include space for a sign or a NUL terminator. + */ +int s_mp_outlen(int bits, int r) +{ + return (int)((double)bits * LOG_V_2(r)); + +} /* end s_mp_outlen() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* HERE THERE BE DRAGONS */ +/* crc==4242132123, version==2, Sat Feb 02 06:43:52 2002 */ diff --git a/mtest/mpi.h b/mtest/mpi.h new file mode 100644 index 0000000..f7a3d14 --- /dev/null +++ b/mtest/mpi.h @@ -0,0 +1,227 @@ +/* + mpi.h + + by Michael J. Fromberger + Copyright (C) 1998 Michael J. Fromberger, All Rights Reserved + + Arbitrary precision integer arithmetic library + + $Id: mpi.h,v 1.15 2001/09/17 14:16:22 sting Exp $ + */ + +#ifndef _H_MPI_ +#define _H_MPI_ + +#include "mpi-config.h" + +#define MP_LT -1 +#define MP_EQ 0 +#define MP_GT 1 + +#if MP_DEBUG +#undef MP_IOFUNC +#define MP_IOFUNC 1 +#endif + +#if MP_IOFUNC +#include +#include +#endif + +#include + +#define MP_NEG 1 +#define MP_ZPOS 0 + +/* Included for compatibility... */ +#define NEG MP_NEG +#define ZPOS MP_ZPOS + +#define MP_OKAY 0 /* no error, all is well */ +#define MP_YES 0 /* yes (boolean result) */ +#define MP_NO -1 /* no (boolean result) */ +#define MP_MEM -2 /* out of memory */ +#define MP_RANGE -3 /* argument out of range */ +#define MP_BADARG -4 /* invalid parameter */ +#define MP_UNDEF -5 /* answer is undefined */ +#define MP_LAST_CODE MP_UNDEF + +#include "mpi-types.h" + +/* Included for compatibility... */ +#define DIGIT_BIT MP_DIGIT_BIT +#define DIGIT_MAX MP_DIGIT_MAX + +/* Macros for accessing the mp_int internals */ +#define SIGN(MP) ((MP)->sign) +#define USED(MP) ((MP)->used) +#define ALLOC(MP) ((MP)->alloc) +#define DIGITS(MP) ((MP)->dp) +#define DIGIT(MP,N) (MP)->dp[(N)] + +#if MP_ARGCHK == 1 +#define ARGCHK(X,Y) {if(!(X)){return (Y);}} +#elif MP_ARGCHK == 2 +#include +#define ARGCHK(X,Y) assert(X) +#else +#define ARGCHK(X,Y) /* */ +#endif + +/* This defines the maximum I/O base (minimum is 2) */ +#define MAX_RADIX 64 + +typedef struct { + mp_sign sign; /* sign of this quantity */ + mp_size alloc; /* how many digits allocated */ + mp_size used; /* how many digits used */ + mp_digit *dp; /* the digits themselves */ +} mp_int; + +/*------------------------------------------------------------------------*/ +/* Default precision */ + +unsigned int mp_get_prec(void); +void mp_set_prec(unsigned int prec); + +/*------------------------------------------------------------------------*/ +/* Memory management */ + +mp_err mp_init(mp_int *mp); +mp_err mp_init_array(mp_int mp[], int count); +mp_err mp_init_size(mp_int *mp, mp_size prec); +mp_err mp_init_copy(mp_int *mp, mp_int *from); +mp_err mp_copy(mp_int *from, mp_int *to); +void mp_exch(mp_int *mp1, mp_int *mp2); +void mp_clear(mp_int *mp); +void mp_clear_array(mp_int mp[], int count); +void mp_zero(mp_int *mp); +void mp_set(mp_int *mp, mp_digit d); +mp_err mp_set_int(mp_int *mp, long z); +mp_err mp_shrink(mp_int *a); + + +/*------------------------------------------------------------------------*/ +/* Single digit arithmetic */ + +mp_err mp_add_d(mp_int *a, mp_digit d, mp_int *b); +mp_err mp_sub_d(mp_int *a, mp_digit d, mp_int *b); +mp_err mp_mul_d(mp_int *a, mp_digit d, mp_int *b); +mp_err mp_mul_2(mp_int *a, mp_int *c); +mp_err mp_div_d(mp_int *a, mp_digit d, mp_int *q, mp_digit *r); +mp_err mp_div_2(mp_int *a, mp_int *c); +mp_err mp_expt_d(mp_int *a, mp_digit d, mp_int *c); + +/*------------------------------------------------------------------------*/ +/* Sign manipulations */ + +mp_err mp_abs(mp_int *a, mp_int *b); +mp_err mp_neg(mp_int *a, mp_int *b); + +/*------------------------------------------------------------------------*/ +/* Full arithmetic */ + +mp_err mp_add(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_sub(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_mul(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_mul_2d(mp_int *a, mp_digit d, mp_int *c); +#if MP_SQUARE +mp_err mp_sqr(mp_int *a, mp_int *b); +#else +#define mp_sqr(a, b) mp_mul(a, a, b) +#endif +mp_err mp_div(mp_int *a, mp_int *b, mp_int *q, mp_int *r); +mp_err mp_div_2d(mp_int *a, mp_digit d, mp_int *q, mp_int *r); +mp_err mp_expt(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_2expt(mp_int *a, mp_digit k); +mp_err mp_sqrt(mp_int *a, mp_int *b); + +/*------------------------------------------------------------------------*/ +/* Modular arithmetic */ + +#if MP_MODARITH +mp_err mp_mod(mp_int *a, mp_int *m, mp_int *c); +mp_err mp_mod_d(mp_int *a, mp_digit d, mp_digit *c); +mp_err mp_addmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +mp_err mp_submod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +mp_err mp_mulmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +#if MP_SQUARE +mp_err mp_sqrmod(mp_int *a, mp_int *m, mp_int *c); +#else +#define mp_sqrmod(a, m, c) mp_mulmod(a, a, m, c) +#endif +mp_err mp_exptmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +mp_err mp_exptmod_d(mp_int *a, mp_digit d, mp_int *m, mp_int *c); +#endif /* MP_MODARITH */ + +/*------------------------------------------------------------------------*/ +/* Comparisons */ + +int mp_cmp_z(mp_int *a); +int mp_cmp_d(mp_int *a, mp_digit d); +int mp_cmp(mp_int *a, mp_int *b); +int mp_cmp_mag(mp_int *a, mp_int *b); +int mp_cmp_int(mp_int *a, long z); +int mp_isodd(mp_int *a); +int mp_iseven(mp_int *a); + +/*------------------------------------------------------------------------*/ +/* Number theoretic */ + +#if MP_NUMTH +mp_err mp_gcd(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_lcm(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_xgcd(mp_int *a, mp_int *b, mp_int *g, mp_int *x, mp_int *y); +mp_err mp_invmod(mp_int *a, mp_int *m, mp_int *c); +#endif /* end MP_NUMTH */ + +/*------------------------------------------------------------------------*/ +/* Input and output */ + +#if MP_IOFUNC +void mp_print(mp_int *mp, FILE *ofp); +#endif /* end MP_IOFUNC */ + +/*------------------------------------------------------------------------*/ +/* Base conversion */ + +#define BITS 1 +#define BYTES CHAR_BIT + +mp_err mp_read_signed_bin(mp_int *mp, unsigned char *str, int len); +int mp_signed_bin_size(mp_int *mp); +mp_err mp_to_signed_bin(mp_int *mp, unsigned char *str); + +mp_err mp_read_unsigned_bin(mp_int *mp, unsigned char *str, int len); +int mp_unsigned_bin_size(mp_int *mp); +mp_err mp_to_unsigned_bin(mp_int *mp, unsigned char *str); + +int mp_count_bits(mp_int *mp); + +#if MP_COMPAT_MACROS +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp) mp_signed_bin_size(mp) +#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp) mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) +#endif + +mp_err mp_read_radix(mp_int *mp, unsigned char *str, int radix); +int mp_radix_size(mp_int *mp, int radix); +int mp_value_radix_size(int num, int qty, int radix); +mp_err mp_toradix(mp_int *mp, unsigned char *str, int radix); + +int mp_char2value(char ch, int r); + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S) mp_toradix((M), (S), 16) + +/*------------------------------------------------------------------------*/ +/* Error strings */ + +const char *mp_strerror(mp_err ec); + +#endif /* end _H_MPI_ */ diff --git a/mtest/mtest.c b/mtest/mtest.c new file mode 100644 index 0000000..2c24825 --- /dev/null +++ b/mtest/mtest.c @@ -0,0 +1,303 @@ +/* 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 + + */ + +#ifdef MP_8BIT +#define THE_MASK 127 +#else +#define THE_MASK 32767 +#endif + +#include +#include +#include +#include "mpi.c" + +FILE *rng; + +void rand_num(mp_int *a) +{ + int n, size; + unsigned char buf[2048]; + + size = 1 + ((fgetc(rng)<<8) + fgetc(rng)) % 1031; + buf[0] = (fgetc(rng)&1)?1:0; + fread(buf+1, 1, size, rng); + while (buf[1] == 0) buf[1] = fgetc(rng); + mp_read_raw(a, buf, 1+size); +} + +void rand_num2(mp_int *a) +{ + int n, size; + unsigned char buf[2048]; + + size = 1 + ((fgetc(rng)<<8) + fgetc(rng)) % 97; + buf[0] = (fgetc(rng)&1)?1:0; + fread(buf+1, 1, size, rng); + while (buf[1] == 0) buf[1] = fgetc(rng); + mp_read_raw(a, buf, 1+size); +} + +#define mp_to64(a, b) mp_toradix(a, b, 64) + +int main(void) +{ + int n, tmp; + mp_int a, b, c, d, e; + clock_t t1; + char buf[4096]; + + mp_init(&a); + mp_init(&b); + mp_init(&c); + mp_init(&d); + mp_init(&e); + + + /* initial (2^n - 1)^2 testing, makes sure the comba multiplier works [it has the new carry code] */ +/* + mp_set(&a, 1); + for (n = 1; n < 8192; n++) { + mp_mul(&a, &a, &c); + printf("mul\n"); + mp_to64(&a, buf); + printf("%s\n%s\n", buf, buf); + mp_to64(&c, buf); + printf("%s\n", buf); + + mp_add_d(&a, 1, &a); + mp_mul_2(&a, &a); + mp_sub_d(&a, 1, &a); + } +*/ + + 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; + } + } + + t1 = clock(); + for (;;) { + if (clock() - t1 > CLOCKS_PER_SEC) { + sleep(2); + t1 = clock(); + } + + n = fgetc(rng) % 15; + + if (n == 0) { + /* add tests */ + rand_num(&a); + rand_num(&b); + mp_add(&a, &b, &c); + printf("add\n"); + mp_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&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_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&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_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&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_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&c, buf); + printf("%s\n", buf); + mp_to64(&d, buf); + printf("%s\n", buf); + } else if (n == 4) { + /* sqr tests */ + rand_num(&a); + mp_sqr(&a, &b); + printf("sqr\n"); + mp_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&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_to64(&a, buf); + printf("mul2d\n"); + printf("%s\n", buf); + printf("%d\n", n); + mp_to64(&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_to64(&a, buf); + printf("div2d\n"); + printf("%s\n", buf); + printf("%d\n", n); + mp_to64(&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_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&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_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&c, buf); + printf("%s\n", buf); + } else if (n == 9) { + /* exptmod test */ + rand_num2(&a); + rand_num2(&b); + rand_num2(&c); +// if (c.dp[0]&1) mp_add_d(&c, 1, &c); + a.sign = b.sign = c.sign = 0; + mp_exptmod(&a, &b, &c, &d); + printf("expt\n"); + mp_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&c, buf); + printf("%s\n", buf); + mp_to64(&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_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + mp_to64(&c, buf); + printf("%s\n", buf); + } else if (n == 11) { + rand_num(&a); + mp_mul_2(&a, &a); + mp_div_2(&a, &b); + printf("div2\n"); + mp_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + } else if (n == 12) { + rand_num2(&a); + mp_mul_2(&a, &b); + printf("mul2\n"); + mp_to64(&a, buf); + printf("%s\n", buf); + mp_to64(&b, buf); + printf("%s\n", buf); + } else if (n == 13) { + rand_num2(&a); + tmp = abs(rand()) & THE_MASK; + mp_add_d(&a, tmp, &b); + printf("add_d\n"); + mp_to64(&a, buf); + printf("%s\n%d\n", buf, tmp); + mp_to64(&b, buf); + printf("%s\n", buf); + } else if (n == 14) { + rand_num2(&a); + tmp = abs(rand()) & THE_MASK; + mp_sub_d(&a, tmp, &b); + printf("sub_d\n"); + mp_to64(&a, buf); + printf("%s\n%d\n", buf, tmp); + mp_to64(&b, buf); + printf("%s\n", buf); + } + } + fclose(rng); + return 0; +} diff --git a/pics/design_process.sxd b/pics/design_process.sxd new file mode 100644 index 0000000000000000000000000000000000000000..7414dbb27696fe716fe8c73a5c2b21d8c3e0c23c GIT binary patch literal 6950 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 literal 0 HcmV?d00001 diff --git a/pics/design_process.tif b/pics/design_process.tif new file mode 100644 index 0000000000000000000000000000000000000000..4a0c012d565ecd12a43186f132b3bccdefb9a23e GIT binary patch literal 79042 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 literal 0 HcmV?d00001 diff --git a/pics/expt_state.sxd b/pics/expt_state.sxd new file mode 100644 index 0000000000000000000000000000000000000000..6518404d5fa6eca01b4457a813f85cda074c0915 GIT binary patch literal 6869 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 YjJ7)dxd~WU_~#EhHWpUWwTse!03-Pj!~g&Q literal 0 HcmV?d00001 diff --git a/pics/expt_state.tif b/pics/expt_state.tif new file mode 100644 index 0000000000000000000000000000000000000000..cb06e8edf17017dec815be222c845f1b0a938e07 GIT binary patch literal 87542 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≦RYV{r={iMn(Ta_rSwB@dOoKkT7uoB zm>DX9w4o@~>(T4B6{=OK>K|NB_%L#N5_JJ$;s!Q}qJxUPIv=G3bmrL^6**GGA-Da^ zCB;W@?#|o}7gXp1E%T~qJTaVw0a<51tcNsB*noTLp+>XomZ1Ci_CA7uD{ibGLLbrc zT5N_{Zw1xom9I4Oe+VzQ2k5ll zA-%4~x&F@$@1$Lx7H*QE4ps;-706f=e)ER!+<0`&_7;vu*;d5e1Hdmg$Ck3Xz9$sC zT>ES{8$3`HsFZJ6Uks3y*oEzf$AQ*8vU5r+r>OaZO5$V<)eAq!DSxli{Vj1eh?-@R zM7k^F;Y|5aSD@rah|h1E#LmpZ{%oA72Ak^saDSUZe9flmts$QgjZS-A>rwtj1TI?h zJ^0)V-fJB_dBC+fRKDKg8^@9qC

Gz?-?J@iV6)5~XACr^a8KN7>Cj@YUK!?WXXd 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 design_process.ps + +sliding_window.ps: sliding_window.tif + tiff2ps -s -e sliding_window.tif > sliding_window.ps + +expt_state.ps: expt_state.tif + tiff2ps -s -e expt_state.tif > expt_state.ps + +primality.ps: primality.tif + tiff2ps -s -e primality.tif > primality.ps + +design_process.pdf: design_process.ps + epstopdf design_process.ps + +sliding_window.pdf: sliding_window.ps + epstopdf sliding_window.ps + +expt_state.pdf: expt_state.ps + epstopdf expt_state.ps + +primality.pdf: primality.ps + epstopdf primality.ps + + +pses: sliding_window.ps expt_state.ps primality.ps design_process.ps +pdfes: sliding_window.pdf expt_state.pdf primality.pdf design_process.pdf + +clean: + rm -rf *.ps *.pdf .xvpics + \ No newline at end of file diff --git a/pics/primality.tif b/pics/primality.tif new file mode 100644 index 0000000000000000000000000000000000000000..76d6be3fac2cc8b07a401e008fbe45f02758cef7 GIT binary patch literal 85514 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 literal 0 HcmV?d00001 diff --git a/pics/radix.sxd b/pics/radix.sxd new file mode 100644 index 0000000000000000000000000000000000000000..b9eb9a032d0b5229d4828911466490a95c126a2b GIT binary patch literal 6181 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 D!-Jt8 literal 0 HcmV?d00001 diff --git a/pics/sliding_window.sxd b/pics/sliding_window.sxd new file mode 100644 index 0000000000000000000000000000000000000000..91e7c0d9f43b612ff0a59d86a71c8928fbd695f3 GIT binary patch literal 6787 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 literal 0 HcmV?d00001 diff --git a/pics/sliding_window.tif b/pics/sliding_window.tif new file mode 100644 index 0000000000000000000000000000000000000000..bb4cb96ebff4e06478672dd3583bd26a51d85b5d GIT binary patch literal 53880 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 DSo1$6 literal 0 HcmV?d00001 diff --git a/pre_gen/mpi.c b/pre_gen/mpi.c new file mode 100644 index 0000000..1f9997f --- /dev/null +++ b/pre_gen/mpi.c @@ -0,0 +1,8414 @@ +/* Start: bn_error.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +static const struct { + int code; + char *msg; +} msgs[] = { + { MP_OKAY, "Successful" }, + { MP_MEM, "Out of heap" }, + { MP_VAL, "Value out of range" } +}; + +/* return a char * string for a given code */ +char *mp_error_to_string(int code) +{ + int x; + + /* scan the lookup table for the given message */ + for (x = 0; x < (int)(sizeof(msgs) / sizeof(msgs[0])); x++) { + if (msgs[x].code == code) { + return msgs[x].msg; + } + } + + /* generic reply for invalid code */ + return "Invalid error code"; +} + + +/* End: bn_error.c */ + +/* Start: bn_fast_mp_invmod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes the modular inverse via binary extended euclidean algorithm, + * that is c = 1/a mod b + * + * Based on mp_invmod except this is optimized for the case where b is + * odd as per HAC Note 14.64 on pp. 610 + */ +int +fast_mp_invmod (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int x, y, u, v, B, D; + int res, neg; + + /* 2. [modified] b must be odd */ + if (mp_iseven (b) == 1) { + return MP_VAL; + } + + /* init all our temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x == modulus, y == value to invert */ + if ((res = mp_copy (b, &x)) != MP_OKAY) { + goto __ERR; + } + + /* we need y = |a| */ + if ((res = mp_abs (a, &y)) != MP_OKAY) { + goto __ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto __ERR; + } + 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 __ERR; + } + /* 4.2 if B is odd then */ + if (mp_isodd (&B) == 1) { + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto __ERR; + } + } + /* B = B/2 */ + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + /* 5.2 if D is odd then */ + if (mp_isodd (&D) == 1) { + /* D = (D-x)/2 */ + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto __ERR; + } + } + /* D = D/2 */ + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 6. if u >= v then */ + if (mp_cmp (&u, &v) != MP_LT) { + /* u = u - v, B = B - D */ + if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto __ERR; + } + } else { + /* v - v - u, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + + /* b is now the inverse */ + neg = a->sign; + while (D.sign == MP_NEG) { + if ((res = mp_add (&D, b, &D)) != MP_OKAY) { + goto __ERR; + } + } + mp_exch (&D, c); + c->sign = neg; + res = MP_OKAY; + +__ERR:mp_clear_multi (&x, &y, &u, &v, &B, &D, NULL); + return res; +} + +/* End: bn_fast_mp_invmod.c */ + +/* Start: bn_fast_mp_montgomery_reduce.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of mp_montgomery_reduce + * which uses the comba method to quickly calculate the columns of the + * reduction. + * + * Based on Algorithm 14.32 on pp.601 of HAC. +*/ +int +fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, olduse; + mp_word W[MP_WARRAY]; + + /* get old used count */ + olduse = x->used; + + /* grow a as required */ + if (x->alloc < n->used + 1) { + if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { + return res; + } + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + { + register mp_word *_W; + register mp_digit *tmpx; + + /* alias for the W[] array */ + _W = W; + + /* alias for the digits of x*/ + tmpx = x->dp; + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + *_W++ = *tmpx++; + } + + /* zero the high words of W[a->used..m->used*2] */ + for (; ix < n->used * 2 + 1; ix++) { + *_W++ = 0; + } + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + /* mu = 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 mu; + mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); + + /* a = a + mu * 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 *tmpn; + register mp_word *_W; + + /* alias for the digits of the modulus */ + tmpn = n->dp; + + /* Alias for the columns set by an offset of ix */ + _W = W + ix; + + /* inner loop */ + for (iy = 0; iy < n->used; iy++) { + *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); + } + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + { + register mp_digit *tmpx; + register mp_word *_W, *_W1; + + /* nox fix rest of carries */ + + /* alias for current word */ + _W1 = W + ix; + + /* alias for next word, where the carry goes */ + _W = W + ++ix; + + for (; ix <= n->used * 2 + 1; ix++) { + *_W++ += *_W1++ >> ((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 + */ + + /* alias for destination word */ + tmpx = x->dp; + + /* alias for shifted double precision result */ + _W = W + n->used; + + for (ix = 0; ix < n->used + 1; ix++) { + *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); + } + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + for (; ix < olduse; ix++) { + *tmpx++ = 0; + } + } + + /* set the max used and clamp */ + x->used = n->used + 1; + mp_clamp (x); + + /* if A >= m then A = A - m */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + return MP_OKAY; +} + +/* End: bn_fast_mp_montgomery_reduce.c */ + +/* Start: bn_fast_s_mp_mul_digs.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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. + * + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature + * required for fast Barrett reduction). + * + * Based on Algorithm 14.12 on pp.595 of HAC. + * + */ +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[MP_WARRAY]; + + /* grow the destination as required */ + 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; + + { + register mp_digit *tmpc; + + /* 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. + * + * In this particular implementation we feed the carries from + * behind which means when the loop terminates we still have one + * last digit to copy + */ + tmpc = c->dp; + for (ix = 1; ix < digs; ix++) { + /* forward the carry from the previous temp */ + W[ix] += (W[ix - 1] >> ((mp_word) DIGIT_BIT)); + + /* now extract the previous digit [below the carry] */ + *tmpc++ = (mp_digit) (W[ix - 1] & ((mp_word) MP_MASK)); + } + /* fetch the last digit */ + *tmpc++ = (mp_digit) (W[digs - 1] & ((mp_word) MP_MASK)); + + /* clear unused digits [that existed in the old copy of c] */ + for (; ix < olduse; ix++) { + *tmpc++ = 0; + } + } + mp_clamp (c); + return MP_OKAY; +} + +/* End: bn_fast_s_mp_mul_digs.c */ + +/* Start: bn_fast_s_mp_mul_high_digs.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ + #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. + * + * Based on Algorithm 14.12 on pp.595 of HAC. + */ +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[MP_WARRAY]; + + /* calculate size of product and allocate more space if required */ + 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; + + /* skip cases below zero where ix > digs */ + if (iy < 0) { + iy = abs(iy); + tmpy += iy; + _W += iy; + iy = 0; + } + + /* 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 + * + * See comments in bn_fast_s_mp_mul_digs.c + */ + 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[newused - 1] = (mp_digit) (W[newused - 1] & ((mp_word) MP_MASK)); + + for (; ix < oldused; ix++) { + c->dp[ix] = 0; + } + mp_clamp (c); + return MP_OKAY; +} + +/* End: bn_fast_s_mp_mul_high_digs.c */ + +/* Start: bn_fast_s_mp_sqr.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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! + * + * Based on Algorithm 14.16 on pp.597 of HAC. + * + */ +int fast_s_mp_sqr (mp_int * a, mp_int * b) +{ + int olduse, newused, res, ix, pa; + mp_word W2[MP_WARRAY], W[MP_WARRAY]; + + /* calculate size of product and allocate as required */ + 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 outer 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 + * + * Note that every outer product is computed + * for a particular column only once which means that + * there is no need todo a double precision addition + * into the W2[] array. + */ + 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; + + /* now compute digits + * + * We have to double the inner product sums, add in the + * outer product sums, propagate carries and convert + * to single precision. + */ + { + register mp_digit *tmpb; + + /* double first value, since the inner products are + * half of what they should be + */ + W[0] += W[0] + W2[0]; + + tmpb = b->dp; + for (ix = 1; ix < newused; ix++) { + /* double/add next digit */ + W[ix] += W[ix] + W2[ix]; + + /* propagate carry forwards [from the previous digit] */ + W[ix] = W[ix] + (W[ix - 1] >> ((mp_word) DIGIT_BIT)); + + /* store the current digit now that the carry isn't + * needed + */ + *tmpb++ = (mp_digit) (W[ix - 1] & ((mp_word) MP_MASK)); + } + /* set the last value. Note even if the carry is zero + * this is required since the next step will not zero + * it if b originally had a value at b->dp[2*a.used] + */ + *tmpb++ = (mp_digit) (W[(newused) - 1] & ((mp_word) MP_MASK)); + + /* clear high digits of b if there were any originally */ + for (; ix < olduse; ix++) { + *tmpb++ = 0; + } + } + + mp_clamp (b); + return MP_OKAY; +} + +/* End: bn_fast_s_mp_sqr.c */ + +/* Start: bn_mp_2expt.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +int +mp_2expt (mp_int * a, int b) +{ + int res; + + /* zero a as per default */ + mp_zero (a); + + /* grow a to accomodate the single bit */ + if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { + return res; + } + + /* set the used count of where the bit will go */ + a->used = b / DIGIT_BIT + 1; + + /* put the single bit in its place */ + a->dp[b / DIGIT_BIT] = 1 << (b % DIGIT_BIT); + + return MP_OKAY; +} + +/* End: bn_mp_2expt.c */ + +/* Start: bn_mp_abs.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +int +mp_abs (mp_int * a, mp_int * b) +{ + int res; + + /* copy a to b */ + if (a != b) { + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} + +/* End: bn_mp_abs.c */ + +/* Start: bn_mp_add.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* high level addition (handles signs) */ +int mp_add (mp_int * a, mp_int * b, mp_int * c) +{ + int sa, sb, res; + + /* get sign of both inputs */ + sa = a->sign; + sb = b->sign; + + /* handle two cases, not four */ + if (sa == sb) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* one positive, the other negative */ + /* subtract the one with the greater magnitude from */ + /* the one of the lesser magnitude. The result gets */ + /* the sign of the one with the greater magnitude. */ + if (mp_cmp_mag (a, b) == MP_LT) { + c->sign = sb; + res = s_mp_sub (b, a, c); + } else { + c->sign = sa; + res = s_mp_sub (a, b, c); + } + } + return res; +} + + +/* End: bn_mp_add.c */ + +/* Start: bn_mp_add_d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* single digit addition */ +int +mp_add_d (mp_int * a, mp_digit b, mp_int * c) +{ + int res, ix, oldused; + mp_digit *tmpa, *tmpc, mu; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative and |a| >= b, call c = |a| - b */ + if (a->sign == MP_NEG && (a->used > 1 || a->dp[0] >= b)) { + /* temporarily fix sign of a */ + a->sign = MP_ZPOS; + + /* c = |a| - b */ + res = mp_sub_d(a, b, c); + + /* fix sign */ + a->sign = c->sign = MP_NEG; + + return res; + } + + /* old number of used digits in c */ + oldused = c->used; + + /* sign always positive */ + c->sign = MP_ZPOS; + + /* source alias */ + tmpa = a->dp; + + /* destination alias */ + tmpc = c->dp; + + /* if a is positive */ + if (a->sign == MP_ZPOS) { + /* add digit, after this we're propagating + * the carry. + */ + *tmpc = *tmpa++ + b; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + + /* now handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ + mu; + mu = *tmpc >> DIGIT_BIT; + *tmpc++ &= MP_MASK; + } + /* set final carry */ + ix++; + *tmpc++ = mu; + + /* setup size */ + c->used = a->used + 1; + } else { + /* a was negative and |a| < b */ + c->used = 1; + + /* the result is a single digit */ + if (a->used == 1) { + *tmpc++ = b - a->dp[0]; + } else { + *tmpc++ = b; + } + + /* setup count so the clearing of oldused + * can fall through correctly + */ + ix = 1; + } + + /* now zero to oldused */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + + return MP_OKAY; +} + + +/* End: bn_mp_add_d.c */ + +/* Start: bn_mp_addmod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_addmod.c */ + +/* Start: bn_mp_and.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_and.c */ + +/* Start: bn_mp_clamp.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* trim unused digits + * + * This is used to ensure that leading zero digits are + * trimed and the leading "used" digit will be non-zero + * Typically very fast. Also fixes the sign if there + * are no more leading digits + */ +void +mp_clamp (mp_int * a) +{ + /* decrease used while the most significant digit is + * zero. + */ + while (a->used > 0 && a->dp[a->used - 1] == 0) { + --(a->used); + } + + /* reset the sign flag if used == 0 */ + if (a->used == 0) { + a->sign = MP_ZPOS; + } +} + +/* End: bn_mp_clamp.c */ + +/* Start: bn_mp_clear.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* clear one (frees) */ +void +mp_clear (mp_int * a) +{ + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* first zero the digits */ + memset (a->dp, 0, sizeof (mp_digit) * a->used); + + /* free ram */ + XFREE(a->dp); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} + +/* End: bn_mp_clear.c */ + +/* Start: bn_mp_clear_multi.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include +#include + +void mp_clear_multi(mp_int *mp, ...) +{ + mp_int* next_mp = mp; + va_list args; + va_start(args, mp); + while (next_mp != NULL) { + mp_clear(next_mp); + next_mp = va_arg(args, mp_int*); + } + va_end(args); +} + +/* End: bn_mp_clear_multi.c */ + +/* Start: bn_mp_cmp.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* compare two ints (signed)*/ +int +mp_cmp (mp_int * a, mp_int * b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + if (a->sign == MP_NEG) { + return MP_LT; + } else { + return MP_GT; + } + } + + /* compare digits */ + if (a->sign == MP_NEG) { + /* if negative compare opposite direction */ + return mp_cmp_mag(b, a); + } else { + return mp_cmp_mag(a, b); + } +} + +/* End: bn_mp_cmp.c */ + +/* Start: bn_mp_cmp_d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* compare a digit */ +int mp_cmp_d(mp_int * a, mp_digit b) +{ + /* compare based on sign */ + if (a->sign == MP_NEG) { + return MP_LT; + } + + /* compare based on magnitude */ + if (a->used > 1) { + return MP_GT; + } + + /* compare the only digit of a to b */ + if (a->dp[0] > b) { + return MP_GT; + } else if (a->dp[0] < b) { + return MP_LT; + } else { + return MP_EQ; + } +} + +/* End: bn_mp_cmp_d.c */ + +/* Start: bn_mp_cmp_mag.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* compare maginitude of two ints (unsigned) */ +int mp_cmp_mag (mp_int * a, mp_int * b) +{ + int n; + mp_digit *tmpa, *tmpb; + + /* compare based on # of non-zero digits */ + if (a->used > b->used) { + return MP_GT; + } + + if (a->used < b->used) { + return MP_LT; + } + + /* alias for a */ + tmpa = a->dp + (a->used - 1); + + /* alias for b */ + tmpb = b->dp + (a->used - 1); + + /* compare based on digits */ + for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { + if (*tmpa > *tmpb) { + return MP_GT; + } + + if (*tmpa < *tmpb) { + return MP_LT; + } + } + return MP_EQ; +} + +/* End: bn_mp_cmp_mag.c */ + +/* Start: bn_mp_cnt_lsb.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +static const int lnz[16] = { + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 +}; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a) +{ + int x; + mp_digit q, qq; + + /* easy out */ + if (mp_iszero(a) == 1) { + return 0; + } + + /* scan lower digits until non-zero */ + for (x = 0; x < a->used && a->dp[x] == 0; x++); + q = a->dp[x]; + x *= DIGIT_BIT; + + /* now scan this digit until a 1 is found */ + if ((q & 1) == 0) { + do { + qq = q & 15; + x += lnz[qq]; + q >>= 4; + } while (qq == 0); + } + return x; +} + + +/* End: bn_mp_cnt_lsb.c */ + +/* Start: bn_mp_copy.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* copy, b = a */ +int +mp_copy (mp_int * a, mp_int * b) +{ + int res, n; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + /* zero b and copy the parameters over */ + { + register mp_digit *tmpa, *tmpb; + + /* pointer aliases */ + + /* source */ + tmpa = a->dp; + + /* destination */ + tmpb = b->dp; + + /* copy all the digits */ + for (n = 0; n < a->used; n++) { + *tmpb++ = *tmpa++; + } + + /* clear high digits */ + for (; n < b->used; n++) { + *tmpb++ = 0; + } + } + + /* copy used count and sign */ + b->used = a->used; + b->sign = a->sign; + return MP_OKAY; +} + +/* End: bn_mp_copy.c */ + +/* Start: bn_mp_count_bits.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* returns the number of bits in an int */ +int +mp_count_bits (mp_int * a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (a->used == 0) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > ((mp_digit) 0)) { + ++r; + q >>= ((mp_digit) 1); + } + return r; +} + +/* End: bn_mp_count_bits.c */ + +/* Start: bn_mp_div.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 = mp_count_bits(&y) % DIGIT_BIT; + if (norm < (int)(DIGIT_BIT-1)) { + norm = (DIGIT_BIT-1) - norm; + if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { + goto __Y; + } + if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { + goto __Y; + } + } else { + norm = 0; + } + + /* 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; + + /* 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.used) { + 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] = ((((mp_digit)1) << DIGIT_BIT) - 1); + } 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)); + } + + /* 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_mag(&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; + } + + /* 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_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; +} + +/* End: bn_mp_div.c */ + +/* Start: bn_mp_div_2.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = a/2 */ +int mp_div_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* copy */ + if (b->alloc < a->used) { + if ((res = mp_grow (b, a->used)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* source alias */ + tmpa = a->dp + b->used - 1; + + /* dest alias */ + tmpb = b->dp + b->used - 1; + + /* carry */ + r = 0; + for (x = b->used - 1; x >= 0; x--) { + /* get the carry for the next iteration */ + rr = *tmpa & 1; + + /* shift the current digit, add in carry and store */ + *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + mp_clamp (b); + return MP_OKAY; +} + +/* End: bn_mp_div_2.c */ + +/* Start: bn_mp_div_2d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* shift right by a certain bit count (store quotient in c, optional 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 */ + if (b >= (int)DIGIT_BIT) { + mp_rshd (c, b / DIGIT_BIT); + } + + /* shift any bit count < DIGIT_BIT */ + D = (mp_digit) (b % DIGIT_BIT); + if (D != 0) { + register mp_digit *tmpc, mask, shift; + + /* mask */ + mask = (((mp_digit)1) << D) - 1; + + /* shift for lsb */ + shift = DIGIT_BIT - D; + + /* alias */ + tmpc = c->dp + (c->used - 1); + + /* carry */ + r = 0; + for (x = c->used - 1; x >= 0; x--) { + /* get the lower bits of this word in a temp */ + rr = *tmpc & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + *tmpc = (*tmpc >> D) | (r << shift); + --tmpc; + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp (c); + if (d != NULL) { + mp_exch (&t, d); + } + mp_clear (&t); + return MP_OKAY; +} + +/* End: bn_mp_div_2d.c */ + +/* Start: bn_mp_div_3.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* divide by three (based on routine from MPI and the GMP manual) */ +int +mp_div_3 (mp_int * a, mp_int *c, mp_digit * d) +{ + mp_int q; + mp_word w, t; + mp_digit b; + int res, ix; + + /* b = 2**DIGIT_BIT / 3 */ + b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); + + if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= 3) { + /* multiply w by [1/3] */ + t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); + + /* now subtract 3 * [w/3] from w, to get the remainder */ + w -= t+t+t; + + /* fixup the remainder as required since + * the optimization is not exact. + */ + while (w >= 3) { + t += 1; + w -= 3; + } + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + /* [optional] store the remainder */ + if (d != NULL) { + *d = (mp_digit)w; + } + + /* [optional] store the quotient */ + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + + +/* End: bn_mp_div_3.c */ + +/* Start: bn_mp_div_d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +static int s_is_power_of_two(mp_digit b, int *p) +{ + int x; + + for (x = 1; x < DIGIT_BIT; x++) { + if (b == (((mp_digit)1)<dp[0] & ((1<used)) != MP_OKAY) { + return res; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used - 1; ix >= 0; ix--) { + w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); + + if (w >= b) { + t = (mp_digit)(w / b); + w -= ((mp_word)t) * ((mp_word)b); + } else { + t = 0; + } + q.dp[ix] = (mp_digit)t; + } + + if (d != NULL) { + *d = (mp_digit)w; + } + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return res; +} + + +/* End: bn_mp_div_d.c */ + +/* Start: bn_mp_dr_is_modulus.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines if a number is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a) +{ + int ix; + + /* must be at least two digits */ + if (a->used < 2) { + return 0; + } + + /* must be of the form b**k - a [a <= b] so all + * but the first digit must be equal to -1 (mod b). + */ + for (ix = 1; ix < a->used; ix++) { + if (a->dp[ix] != MP_MASK) { + return 0; + } + } + return 1; +} + + +/* End: bn_mp_dr_is_modulus.c */ + +/* Start: bn_mp_dr_reduce.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* reduce "x" in place modulo "n" using the Diminished Radix algorithm. + * + * Based on algorithm from the paper + * + * "Generating Efficient Primes for Discrete Log Cryptosystems" + * Chae Hoon Lim, Pil Loong Lee, + * POSTECH Information Research Laboratories + * + * The modulus must be of a special format [see manual] + * + * Has been modified to use algorithm 7.10 from the LTM book instead + * + * Input x must be in the range 0 <= x <= (n-1)**2 + */ +int +mp_dr_reduce (mp_int * x, mp_int * n, mp_digit k) +{ + int err, i, m; + mp_word r; + mp_digit mu, *tmpx1, *tmpx2; + + /* m = digits in modulus */ + m = n->used; + + /* ensure that "x" has at least 2m digits */ + if (x->alloc < m + m) { + if ((err = mp_grow (x, m + m)) != MP_OKAY) { + return err; + } + } + +/* top of loop, this is where the code resumes if + * another reduction pass is required. + */ +top: + /* aliases for digits */ + /* alias for lower half of x */ + tmpx1 = x->dp; + + /* alias for upper half of x, or x/B**m */ + tmpx2 = x->dp + m; + + /* set carry to zero */ + mu = 0; + + /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ + for (i = 0; i < m; i++) { + r = ((mp_word)*tmpx2++) * ((mp_word)k) + *tmpx1 + mu; + *tmpx1++ = (mp_digit)(r & MP_MASK); + mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); + } + + /* set final carry */ + *tmpx1++ = mu; + + /* zero words above m */ + for (i = m + 1; i < x->used; i++) { + *tmpx1++ = 0; + } + + /* clamp, sub and return */ + mp_clamp (x); + + /* if x >= n then subtract and reduce again + * Each successive "recursion" makes the input smaller and smaller. + */ + if (mp_cmp_mag (x, n) != MP_LT) { + s_mp_sub(x, n, x); + goto top; + } + return MP_OKAY; +} + +/* End: bn_mp_dr_reduce.c */ + +/* Start: bn_mp_dr_setup.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines the setup value */ +void mp_dr_setup(mp_int *a, mp_digit *d) +{ + /* the casts are required if DIGIT_BIT is one less than + * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] + */ + *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - + ((mp_word)a->dp[0])); +} + + +/* End: bn_mp_dr_setup.c */ + +/* Start: bn_mp_exch.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* swap the elements of two integers, for cases where you can't simply swap the + * mp_int pointers around + */ +void +mp_exch (mp_int * a, mp_int * b) +{ + mp_int t; + + t = *a; + *a = *b; + *b = t; +} + +/* End: bn_mp_exch.c */ + +/* Start: bn_mp_expt_d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* calculate c = a**b using a square-multiply algorithm */ +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++) { + /* square */ + if ((res = mp_sqr (c, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + + /* if the bit is set multiply */ + if ((b & (mp_digit) (((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { + if ((res = mp_mul (c, &g, c)) != MP_OKAY) { + mp_clear (&g); + return res; + } + } + + /* shift to next bit */ + b <<= 1; + } + + mp_clear (&g); + return MP_OKAY; +} + +/* End: bn_mp_expt_d.c */ + +/* Start: bn_mp_exptmod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + + +/* this is a shell function that calls either the normal or Montgomery + * exptmod functions. Originally the call to the montgomery code was + * embedded in the normal function but that wasted alot of stack space + * for nothing (since 99% of the time the Montgomery code would be called) + */ +int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + int dr; + + /* modulus P must be positive */ + if (P->sign == MP_NEG) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (X->sign == MP_NEG) { + mp_int tmpG, tmpX; + int err; + + /* first compute 1/G mod P */ + if ((err = mp_init(&tmpG)) != MP_OKAY) { + return err; + } + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + + /* now get |X| */ + if ((err = mp_init(&tmpX)) != MP_OKAY) { + mp_clear(&tmpG); + return err; + } + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* is it a DR modulus? */ + dr = mp_dr_is_modulus(P); + + /* if not, is it a uDR modulus? */ + if (dr == 0) { + dr = mp_reduce_is_2k(P) << 1; + } + + /* if the modulus is odd or dr != 0 use the fast method */ + if (mp_isodd (P) == 1 || dr != 0) { + return mp_exptmod_fast (G, X, P, Y, dr); + } else { + /* otherwise use the generic Barrett reduction technique */ + return s_mp_exptmod (G, X, P, Y); + } +} + + +/* End: bn_mp_exptmod.c */ + +/* Start: bn_mp_exptmod_fast.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 or Diminished Radix reduction [whichever appropriate] + */ + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int +mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + + /* use a pointer to the reduction algorithm. This allows us to use + * one of many reduction algorithms without modding the guts of + * the code with if statements everywhere. + */ + int (*redux)(mp_int*,mp_int*,mp_digit); + + /* 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; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + return err; + } + } + + /* determine and setup reduction code */ + if (redmode == 0) { + /* now setup montgomery */ + if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { + goto __M; + } + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ + if (((P->used * 2 + 1) < MP_WARRAY) && + P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + redux = fast_mp_montgomery_reduce; + } else { + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; + } + } else if (redmode == 1) { + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; + } else { + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { + goto __M; + } + redux = mp_reduce_2k; + } + + /* setup result */ + if ((err = mp_init (&res)) != MP_OKAY) { + goto __M; + } + + /* 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 (redmode == 0) { + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { + goto __RES; + } + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { + goto __RES; + } + } else { + mp_set(&res, 1); + if ((err = mp_mod(G, 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 = redux (&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 = redux (&M[x], P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* set initial mode and bit cnt */ + mode = 0; + bitcnt = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits so break */ + if (digidx == -1) { + break; + } + /* read next digit and reset bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int)DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)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 = redux (&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 */ + /* square first */ + for (x = 0; x < winsize; x++) { + if ((err = mp_sqr (&res, &res)) != MP_OKAY) { + goto __RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* then multiply */ + if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { + goto __RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + + /* empty window and reset */ + bitcpy = 0; + 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 = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + + /* get next bit of the window */ + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { + goto __RES; + } + if ((err = redux (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + } + } + + if (redmode == 0) { + /* fixup result if Montgomery reduction is used + * recall that any value in a Montgomery system is + * actually multiplied by R mod n. So we have + * to reduce one more time to cancel out the factor + * of R. + */ + if ((err = mp_montgomery_reduce (&res, P, mp)) != MP_OKAY) { + goto __RES; + } + } + + /* swap res with Y */ + mp_exch (&res, Y); + err = MP_OKAY; +__RES:mp_clear (&res); +__M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} + +/* End: bn_mp_exptmod_fast.c */ + +/* Start: bn_mp_exteuclid.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Extended euclidean algorithm of (a, b) produces + a*u1 + b*u2 = u3 + */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) +{ + mp_int u1,u2,u3,v1,v2,v3,t1,t2,t3,q,tmp; + int err; + + if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { + return err; + } + + /* initialize, (u1,u2,u3) = (1,0,a) */ + mp_set(&u1, 1); + if ((err = mp_copy(a, &u3)) != MP_OKAY) { goto _ERR; } + + /* initialize, (v1,v2,v3) = (0,1,b) */ + mp_set(&v2, 1); + if ((err = mp_copy(b, &v3)) != MP_OKAY) { goto _ERR; } + + /* loop while v3 != 0 */ + while (mp_iszero(&v3) == MP_NO) { + /* q = u3/v3 */ + if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { goto _ERR; } + + /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ + if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { goto _ERR; } + if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { goto _ERR; } + + /* (u1,u2,u3) = (v1,v2,v3) */ + if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { goto _ERR; } + + /* (v1,v2,v3) = (t1,t2,t3) */ + if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { goto _ERR; } + if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { goto _ERR; } + } + + /* copy result out */ + if (U1 != NULL) { mp_exch(U1, &u1); } + if (U2 != NULL) { mp_exch(U2, &u2); } + if (U3 != NULL) { mp_exch(U3, &u3); } + + err = MP_OKAY; +_ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); + return err; +} + +/* End: bn_mp_exteuclid.c */ + +/* Start: bn_mp_fread.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* read a bigint from a file stream in ASCII */ +int mp_fread(mp_int *a, int radix, FILE *stream) +{ + int err, ch, neg, y; + + /* clear a */ + mp_zero(a); + + /* if first digit is - then set negative */ + ch = fgetc(stream); + if (ch == '-') { + neg = MP_NEG; + ch = fgetc(stream); + } else { + neg = MP_ZPOS; + } + + for (;;) { + /* find y in the radix map */ + for (y = 0; y < radix; y++) { + if (mp_s_rmap[y] == ch) { + break; + } + } + if (y == radix) { + break; + } + + /* shift up and add */ + if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + + ch = fgetc(stream); + } + if (mp_cmp_d(a, 0) != MP_EQ) { + a->sign = neg; + } + + return MP_OKAY; +} + + +/* End: bn_mp_fread.c */ + +/* Start: bn_mp_fwrite.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +int mp_fwrite(mp_int *a, int radix, FILE *stream) +{ + char *buf; + int err, len, x; + + if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { + return err; + } + + buf = OPT_CAST(char) XMALLOC (len); + if (buf == NULL) { + return MP_MEM; + } + + if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { + XFREE (buf); + return err; + } + + for (x = 0; x < len; x++) { + if (fputc(buf[x], stream) == EOF) { + XFREE (buf); + return MP_VAL; + } + } + + XFREE (buf); + return MP_OKAY; +} + + +/* End: bn_mp_fwrite.c */ + +/* Start: bn_mp_gcd.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Greatest Common Divisor using the binary method */ +int mp_gcd (mp_int * a, mp_int * b, mp_int * c) +{ + mp_int u, v; + int k, u_lsb, v_lsb, res; + + /* either zero than gcd is the largest */ + if (mp_iszero (a) == 1 && mp_iszero (b) == 0) { + return mp_abs (b, c); + } + if (mp_iszero (a) == 0 && mp_iszero (b) == 1) { + return mp_abs (a, c); + } + + /* optimized. At this point if a == 0 then + * b must equal zero too + */ + if (mp_iszero (a) == 1) { + mp_zero(c); + return MP_OKAY; + } + + /* get copies of a and b we can modify */ + 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; + + /* B1. Find the common power of two for u and v */ + u_lsb = mp_cnt_lsb(&u); + v_lsb = mp_cnt_lsb(&v); + k = MIN(u_lsb, v_lsb); + + if (k > 0) { + /* divide the power of two out */ + if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { + goto __V; + } + + if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { + goto __V; + } + } + + /* divide any remaining factors of two out */ + if (u_lsb != k) { + if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { + goto __V; + } + } + + if (v_lsb != k) { + if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { + goto __V; + } + } + + while (mp_iszero(&v) == 0) { + /* make sure v is the largest */ + if (mp_cmp_mag(&u, &v) == MP_GT) { + /* swap u and v to make sure v is >= u */ + mp_exch(&u, &v); + } + + /* subtract smallest from largest */ + if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { + goto __V; + } + + /* Divide out all factors of two */ + if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { + goto __V; + } + } + + /* multiply by 2**k which we divided out at the beginning */ + if ((res = mp_mul_2d (&u, k, c)) != MP_OKAY) { + goto __V; + } + c->sign = MP_ZPOS; + res = MP_OKAY; +__V:mp_clear (&u); +__U:mp_clear (&v); + return res; +} + +/* End: bn_mp_gcd.c */ + +/* Start: bn_mp_get_int.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* get the lower 32-bits of an mp_int */ +unsigned long mp_get_int(mp_int * a) +{ + int i; + unsigned long res; + + if (a->used == 0) { + return 0; + } + + /* get number of digits of the lsb we have to read */ + i = MIN(a->used,(int)((sizeof(unsigned long)*CHAR_BIT+DIGIT_BIT-1)/DIGIT_BIT))-1; + + /* get most significant digit of result */ + res = DIGIT(a,i); + + while (--i >= 0) { + res = (res << DIGIT_BIT) | DIGIT(a,i); + } + + /* force result to 32-bits always so it is consistent on non 32-bit platforms */ + return res & 0xFFFFFFFFUL; +} + +/* End: bn_mp_get_int.c */ + +/* Start: bn_mp_grow.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* grow as required */ +int mp_grow (mp_int * a, int size) +{ + int i; + mp_digit *tmp; + + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + /* ensure there are always at least MP_PREC digits extra on top */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* reallocate the array a->dp + * + * We store the return in a temporary variable + * in case the operation failed we don't want + * to overwrite the dp member of a. + */ + tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); + if (tmp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = tmp; + + /* zero excess digits */ + i = a->alloc; + a->alloc = size; + for (; i < a->alloc; i++) { + a->dp[i] = 0; + } + } + return MP_OKAY; +} + +/* End: bn_mp_grow.c */ + +/* Start: bn_mp_init.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* init a new bigint */ +int mp_init (mp_int * a) +{ + /* allocate memory required and clear it */ + a->dp = OPT_CAST(mp_digit) XCALLOC (sizeof (mp_digit), MP_PREC); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the used to zero, allocated digits to the default precision + * and sign to positive */ + a->used = 0; + a->alloc = MP_PREC; + a->sign = MP_ZPOS; + + return MP_OKAY; +} + +/* End: bn_mp_init.c */ + +/* Start: bn_mp_init_copy.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + } + return mp_copy (b, a); +} + +/* End: bn_mp_init_copy.c */ + +/* Start: bn_mp_init_multi.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include +#include + +int mp_init_multi(mp_int *mp, ...) +{ + mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ + int n = 0; /* Number of ok inits */ + mp_int* cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + if (mp_init(cur_arg) != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* end the current list */ + va_end(args); + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n--) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int*); + } + va_end(clean_args); + res = MP_MEM; + break; + } + n++; + cur_arg = va_arg(args, mp_int*); + } + va_end(args); + return res; /* Assumed ok, if error flagged above. */ +} + + +/* End: bn_mp_init_multi.c */ + +/* Start: bn_mp_init_set.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + mp_set(a, b); + return err; +} + +/* End: bn_mp_init_set.c */ + +/* Start: bn_mp_init_set_int.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* initialize and set a digit */ +int mp_init_set_int (mp_int * a, unsigned long b) +{ + int err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + return mp_set_int(a, b); +} + +/* End: bn_mp_init_set_int.c */ + +/* Start: bn_mp_init_size.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* init an mp_init for a given size */ +int mp_init_size (mp_int * a, int size) +{ + /* pad size so there are always extra digits */ + size += (MP_PREC * 2) - (size % MP_PREC); + + /* alloc mem */ + a->dp = OPT_CAST(mp_digit) XCALLOC (sizeof (mp_digit), size); + if (a->dp == NULL) { + return MP_MEM; + } + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + return MP_OKAY; +} + +/* End: bn_mp_init_size.c */ + +/* Start: bn_mp_invmod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* hac 14.61, pp608 */ +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 || mp_iszero(b) == 1) { + return MP_VAL; + } + + /* if the modulus is odd we can use a faster routine instead */ + if (mp_isodd (b) == 1) { + return fast_mp_invmod (a, b, c); + } + + /* init temps */ + if ((res = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return res; + } + + /* x = a, y = b */ + if ((res = mp_copy (a, &x)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_copy (b, &y)) != MP_OKAY) { + goto __ERR; + } + + /* 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 __ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((res = mp_copy (&x, &u)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_copy (&y, &v)) != MP_OKAY) { + goto __ERR; + } + 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 __ERR; + } + /* 4.2 if A or B is odd then */ + if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { + goto __ERR; + } + } + /* A = A/2, B = B/2 */ + if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + /* 5.2 if C or D is odd then */ + if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { + goto __ERR; + } + } + /* C = C/2, D = D/2 */ + if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { + goto __ERR; + } + if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + + if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { + goto __ERR; + } + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { + goto __ERR; + } + + if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { + goto __ERR; + } + } + + /* 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 __ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0) == MP_LT) { + if ((res = mp_add(&C, b, &C)) != MP_OKAY) { + goto __ERR; + } + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { + goto __ERR; + } + } + + /* C is now the inverse */ + mp_exch (&C, c); + res = MP_OKAY; +__ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return res; +} + +/* End: bn_mp_invmod.c */ + +/* Start: bn_mp_is_square.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Check if remainders are possible squares - fast exclude non-squares */ +static const char rem_128[128] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 +}; + +static const char rem_105[105] = { + 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 +}; + +/* Store non-zero to ret if arg is square, and zero if not */ +int mp_is_square(mp_int *arg,int *ret) +{ + int res; + mp_digit c; + mp_int t; + unsigned long r; + + /* Default to Non-square :) */ + *ret = MP_NO; + + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* digits used? (TSD) */ + if (arg->used == 0) { + return MP_OKAY; + } + + /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ + if (rem_128[127 & DIGIT(arg,0)] == 1) { + return MP_OKAY; + } + + /* Next check mod 105 (3*5*7) */ + if ((res = mp_mod_d(arg,105,&c)) != MP_OKAY) { + return res; + } + if (rem_105[c] == 1) { + return MP_OKAY; + } + + /* product of primes less than 2^31 */ + if ((res = mp_init_set_int(&t,11L*13L*17L*19L*23L*29L*31L)) != MP_OKAY) { + return res; + } + if ((res = mp_mod(arg,&t,&t)) != MP_OKAY) { + goto ERR; + } + r = mp_get_int(&t); + /* Check for other prime modules, note it's not an ERROR but we must + * free "t" so the easiest way is to goto ERR. We know that res + * is already equal to MP_OKAY from the mp_mod call + */ + if ( (1L<<(r%11)) & 0x5C4L ) goto ERR; + if ( (1L<<(r%13)) & 0x9E4L ) goto ERR; + if ( (1L<<(r%17)) & 0x5CE8L ) goto ERR; + if ( (1L<<(r%19)) & 0x4F50CL ) goto ERR; + if ( (1L<<(r%23)) & 0x7ACCA0L ) goto ERR; + if ( (1L<<(r%29)) & 0xC2EDD0CL ) goto ERR; + if ( (1L<<(r%31)) & 0x6DE2B848L ) goto ERR; + + /* Final check - is sqr(sqrt(arg)) == arg ? */ + if ((res = mp_sqrt(arg,&t)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&t,&t)) != MP_OKAY) { + goto ERR; + } + + *ret = (mp_cmp_mag(&t,arg) == MP_EQ) ? MP_YES : MP_NO; +ERR:mp_clear(&t); + return res; +} + +/* End: bn_mp_is_square.c */ + +/* Start: bn_mp_jacobi.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes the jacobi c = (a | n) (or Legendre if n is prime) + * HAC pp. 73 Algorithm 2.149 + */ +int mp_jacobi (mp_int * a, mp_int * p, int *c) +{ + mp_int a1, p1; + int k, s, r, res; + mp_digit residue; + + /* if p <= 0 return MP_VAL */ + if (mp_cmp_d(p, 0) != MP_GT) { + return MP_VAL; + } + + /* 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**k */ + if ((res = mp_init_copy (&a1, a)) != MP_OKAY) { + return res; + } + + if ((res = mp_init (&p1)) != MP_OKAY) { + goto __A1; + } + + /* divide out larger power of two */ + k = mp_cnt_lsb(&a1); + if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { + goto __P1; + } + + /* step 4. if e is even set s=1 */ + if ((k & 1) == 0) { + s = 1; + } else { + /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ + residue = p->dp[0] & 7; + + if (residue == 1 || residue == 7) { + s = 1; + } else if (residue == 3 || residue == 5) { + s = -1; + } + } + + /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ + if ( ((p->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 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 (p, &a1, &p1)) != MP_OKAY) { + goto __P1; + } + if ((res = mp_jacobi (&p1, &a1, &r)) != MP_OKAY) { + goto __P1; + } + *c = s * r; + } + + /* done */ + res = MP_OKAY; +__P1:mp_clear (&p1); +__A1:mp_clear (&a1); + return res; +} + +/* End: bn_mp_jacobi.c */ + +/* Start: bn_mp_karatsuba_mul.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 - a0)(b1 - 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, x0y0, x1y1; + int B, err; + + /* default the return code to an error */ + err = MP_MEM; + + /* min # of digits */ + B = MIN (a->used, b->used); + + /* now divide in two */ + B = B >> 1; + + /* 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_size (&t1, B * 2) != MP_OKAY) + goto Y1; + if (mp_init_size (&x0y0, B * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x1y1, B * 2) != 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; + + { + register int x; + register mp_digit *tmpa, *tmpb, *tmpx, *tmpy; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + tmpa = a->dp; + tmpb = b->dp; + + tmpx = x0.dp; + tmpy = y0.dp; + for (x = 0; x < B; x++) { + *tmpx++ = *tmpa++; + *tmpy++ = *tmpb++; + } + + tmpx = x1.dp; + for (x = B; x < a->used; x++) { + *tmpx++ = *tmpa++; + } + + tmpy = y1.dp; + for (x = B; x < b->used; x++) { + *tmpy++ = *tmpb++; + } + } + + /* 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 */ + /* after this x0 is no longer required, free temp [x0==t2]! */ + 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, &x0) != MP_OKAY) + goto X1Y1; /* t2 = y1 - y0 */ + if (mp_mul (&t1, &x0, &t1) != MP_OKAY) + goto X1Y1; /* t1 = (x1 - x0) * (y1 - y0) */ + + /* add x0y0 */ + if (mp_add (&x0y0, &x1y1, &x0) != MP_OKAY) + goto X1Y1; /* t2 = x0y0 + x1y1 */ + if (mp_sub (&x0, &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; + + err = MP_MEM; + + /* min # of digits */ + B = a->used; + + /* now divide in two */ + B = B >> 1; + + /* 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_size (&t1, a->used * 2) != MP_OKAY) + goto X1; + if (mp_init_size (&t2, a->used * 2) != MP_OKAY) + goto T1; + if (mp_init_size (&x0x0, B * 2) != MP_OKAY) + goto T2; + if (mp_init_size (&x1x1, (a->used - B) * 2) != MP_OKAY) + goto X0X0; + + { + register int x; + register mp_digit *dst, *src; + + src = a->dp; + + /* now shift the digits */ + dst = x0.dp; + for (x = 0; x < B; x++) { + *dst++ = *src++; + } + + dst = x1.dp; + for (x = B; x < a->used; x++) { + *dst++ = *src++; + } + } + + 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)**2 */ + 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) * (x1 - x0) */ + + /* add x0y0 */ + if (s_mp_add (&x0x0, &x1x1, &t2) != MP_OKAY) + goto X1X1; /* t2 = x0x0 + x1x1 */ + if (mp_sub (&t2, &t1, &t1) != MP_OKAY) + goto X1X1; /* t1 = x0x0 + x1x1 - (x1-x0)*(x1-x0) */ + + /* shift by B */ + if (mp_lshd (&t1, B) != MP_OKAY) + goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))< + +/* 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 t1, t2; + + + if ((res = mp_init_multi (&t1, &t2, NULL)) != MP_OKAY) { + return res; + } + + /* t1 = get the GCD of the two inputs */ + if ((res = mp_gcd (a, b, &t1)) != MP_OKAY) { + goto __T; + } + + /* divide the smallest by the GCD */ + if (mp_cmp_mag(a, b) == MP_LT) { + /* store quotient in t2 such that t2 * b is the LCM */ + if ((res = mp_div(a, &t1, &t2, NULL)) != MP_OKAY) { + goto __T; + } + res = mp_mul(b, &t2, c); + } else { + /* store quotient in t2 such that t2 * a is the LCM */ + if ((res = mp_div(b, &t1, &t2, NULL)) != MP_OKAY) { + goto __T; + } + res = mp_mul(a, &t2, c); + } + + /* fix the sign to positive */ + c->sign = MP_ZPOS; + +__T: + mp_clear_multi (&t1, &t2, NULL); + return res; +} + +/* End: bn_mp_lcm.c */ + +/* Start: bn_mp_lshd.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 (a->alloc < a->used + b) { + if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { + return res; + } + } + + { + register mp_digit *top, *bottom; + + /* increment the used by the shift amount then copy upwards */ + a->used += b; + + /* top */ + top = a->dp + a->used - 1; + + /* base */ + bottom = a->dp + a->used - 1 - b; + + /* much like mp_rshd this is implemented using a sliding window + * except the window goes the otherway around. Copying from + * the bottom to the top. see bn_mp_rshd.c for more info. + */ + for (x = a->used - 1; x >= b; x--) { + *top-- = *bottom--; + } + + /* zero the lower digits */ + top = a->dp; + for (x = 0; x < b; x++) { + *top++ = 0; + } + } + return MP_OKAY; +} + +/* End: bn_mp_lshd.c */ + +/* Start: bn_mp_mod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 != b->sign) { + res = mp_add (b, &t, c); + } else { + res = MP_OKAY; + mp_exch (&t, c); + } + + mp_clear (&t); + return res; +} + +/* End: bn_mp_mod.c */ + +/* Start: bn_mp_mod_2d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_mod_2d.c */ + +/* Start: bn_mp_mod_d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +int +mp_mod_d (mp_int * a, mp_digit b, mp_digit * c) +{ + return mp_div_d(a, b, NULL, c); +} + +/* End: bn_mp_mod_d.c */ + +/* Start: bn_mp_montgomery_calc_normalization.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* calculates a = B^n mod b for Montgomery reduction + * Where B is the base [e.g. 2^DIGIT_BIT]. + * B^n mod b is computed by first computing + * A = B^(n-1) which doesn't require a reduction but a simple OR. + * then C = A * B = B^n is computed by performing upto DIGIT_BIT + * shifts with subtractions when the result is greater than b. + * + * The method is slightly modified to shift B unconditionally upto just under + * the leading bit of b. This saves alot of multiple precision shifting. + */ +int +mp_montgomery_calc_normalization (mp_int * a, mp_int * b) +{ + int x, bits, res; + + /* how many bits of last digit does b use */ + bits = mp_count_bits (b) % DIGIT_BIT; + + /* compute A = B^(n-1) * 2^(bits-1) */ + if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { + return res; + } + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)DIGIT_BIT; x++) { + if ((res = mp_mul_2 (a, a)) != MP_OKAY) { + return res; + } + if (mp_cmp_mag (a, b) != MP_LT) { + if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { + return res; + } + } + } + + return MP_OKAY; +} + +/* End: bn_mp_montgomery_calc_normalization.c */ + +/* Start: bn_mp_montgomery_reduce.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +int +mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) +{ + int ix, res, digs; + mp_digit mu; + + /* can the fast reduction [comba] method be used? + * + * Note that unlike in mp_mul you're safely allowed *less* + * than the available columns [255 per default] since carries + * are fixed up in the inner loop. + */ + digs = n->used * 2 + 1; + if ((digs < MP_WARRAY) && + n->used < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_mp_montgomery_reduce (x, n, rho); + } + + /* grow the input as required */ + if (x->alloc < digs) { + if ((res = mp_grow (x, digs)) != MP_OKAY) { + return res; + } + } + x->used = digs; + + for (ix = 0; ix < n->used; ix++) { + /* mu = ai * rho mod b + * + * The value of rho must be precalculated via + * bn_mp_montgomery_setup() such that + * it equals -1/n0 mod b this allows the + * following inner loop to reduce the + * input one digit at a time + */ + mu = (mp_digit) (((mp_word)x->dp[ix]) * ((mp_word)rho) & MP_MASK); + + /* a = a + mu * m * b**i */ + { + register int iy; + register mp_digit *tmpn, *tmpx, u; + register mp_word r; + + /* alias for digits of the modulus */ + tmpn = n->dp; + + /* alias for the digits of x [the input] */ + tmpx = x->dp + ix; + + /* set the carry to zero */ + u = 0; + + /* Multiply and add in place */ + for (iy = 0; iy < n->used; iy++) { + /* compute product and sum */ + r = ((mp_word)mu) * ((mp_word)*tmpn++) + + ((mp_word) u) + ((mp_word) * tmpx); + + /* get carry */ + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + + /* fix digit */ + *tmpx++ = (mp_digit)(r & ((mp_word) MP_MASK)); + } + /* At this point the ix'th digit of x should be zero */ + + + /* propagate carries upwards as required*/ + while (u) { + *tmpx += u; + u = *tmpx >> DIGIT_BIT; + *tmpx++ &= MP_MASK; + } + } + } + + /* at this point the n.used'th least + * significant digits of x are all zero + * which means we can shift x to the + * right by n.used digits and the + * residue is unchanged. + */ + + /* x = x/b**n.used */ + mp_clamp(x); + mp_rshd (x, n->used); + + /* if x >= n then x = x - n */ + if (mp_cmp_mag (x, n) != MP_LT) { + return s_mp_sub (x, n, x); + } + + return MP_OKAY; +} + +/* End: bn_mp_montgomery_reduce.c */ + +/* Start: bn_mp_montgomery_setup.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* setups the montgomery reduction stuff */ +int +mp_montgomery_setup (mp_int * n, mp_digit * rho) +{ + mp_digit x, b; + +/* fast inversion mod 2**k + * + * Based on the fact that + * + * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) + * => 2*X*A - X*X*A*A = 1 + * => 2*(1) - (1) = 1 + */ + b = n->dp[0]; + + if ((b & 1) == 0) { + return MP_VAL; + } + + x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2 - b * x; /* here x*a==1 mod 2**8 */ +#if !defined(MP_8BIT) + x *= 2 - b * x; /* here x*a==1 mod 2**16 */ +#endif +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) + x *= 2 - b * x; /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2 - b * x; /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (((mp_digit) 1 << ((mp_digit) DIGIT_BIT)) - x) & MP_MASK; + + return MP_OKAY; +} + +/* End: bn_mp_montgomery_setup.c */ + +/* Start: bn_mp_mul.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* use Toom-Cook? */ + if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { + res = mp_toom_mul(a, b, c); + /* use Karatsuba? */ + } else if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { + res = mp_karatsuba_mul (a, b, c); + } else { + /* can we use the fast multiplier? + * + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of + * digits won't affect carry propagation + */ + int digs = a->used + b->used + 1; + + if ((digs < MP_WARRAY) && + MIN(a->used, b->used) <= + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + res = fast_s_mp_mul_digs (a, b, c, digs); + } else { + res = s_mp_mul (a, b, c); + } + } + c->sign = neg; + return res; +} + +/* End: bn_mp_mul.c */ + +/* Start: bn_mp_mul_2.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = a*2 */ +int mp_mul_2(mp_int * a, mp_int * b) +{ + int x, res, oldused; + + /* grow to accomodate result */ + if (b->alloc < a->used + 1) { + if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { + return res; + } + } + + oldused = b->used; + b->used = a->used; + + { + register mp_digit r, rr, *tmpa, *tmpb; + + /* alias for source */ + tmpa = a->dp; + + /* alias for dest */ + tmpb = b->dp; + + /* carry */ + r = 0; + for (x = 0; x < a->used; x++) { + + /* get what will be the *next* carry bit from the + * MSB of the current digit + */ + rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); + + /* now shift up this digit, add in the carry [from the previous] */ + *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0) { + /* add a MSB which is always 1 at this point */ + *tmpb = 1; + ++(b->used); + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + tmpb = b->dp + b->used; + for (x = b->used; x < oldused; x++) { + *tmpb++ = 0; + } + } + b->sign = a->sign; + return MP_OKAY; +} + +/* End: bn_mp_mul_2.c */ + +/* Start: bn_mp_mul_2d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* shift left by a certain bit count */ +int mp_mul_2d (mp_int * a, int b, mp_int * c) +{ + mp_digit d; + int res; + + /* copy */ + if (a != c) { + if ((res = mp_copy (a, c)) != MP_OKAY) { + return res; + } + } + + if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { + 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 (b >= (int)DIGIT_BIT) { + if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { + return res; + } + } + + /* shift any bit count < DIGIT_BIT */ + d = (mp_digit) (b % DIGIT_BIT); + if (d != 0) { + register mp_digit *tmpc, shift, mask, r, rr; + register int x; + + /* bitmask for carries */ + mask = (((mp_digit)1) << d) - 1; + + /* shift for msbs */ + shift = DIGIT_BIT - d; + + /* alias */ + tmpc = c->dp; + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + rr = (*tmpc >> shift) & mask; + + /* shift the current word and OR in the carry */ + *tmpc = ((*tmpc << d) | r) & MP_MASK; + ++tmpc; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0) { + c->dp[(c->used)++] = r; + } + } + mp_clamp (c); + return MP_OKAY; +} + +/* End: bn_mp_mul_2d.c */ + +/* Start: bn_mp_mul_d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* multiply by a digit */ +int +mp_mul_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit u, *tmpa, *tmpc; + mp_word r; + int ix, res, olduse; + + /* make sure c is big enough to hold a*b */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* get the original destinations used count */ + olduse = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* alias for a->dp [source] */ + tmpa = a->dp; + + /* alias for c->dp [dest] */ + tmpc = c->dp; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); + + /* mask off higher bits to get a single digit */ + *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); + + /* send carry into next iteration */ + u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); + } + + /* store final carry [if any] */ + *tmpc++ = u; + + /* now zero digits above the top */ + while (ix++ < olduse) { + *tmpc++ = 0; + } + + /* set used count */ + c->used = a->used + 1; + mp_clamp(c); + + return MP_OKAY; +} + +/* End: bn_mp_mul_d.c */ + +/* Start: bn_mp_mulmod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_mulmod.c */ + +/* Start: bn_mp_n_root.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* find the n'th root of an integer + * + * Result found such that (c)**b <= a and (c+1)**b > a + * + * This algorithm uses Newton's approximation + * x[i+1] = x[i] - f(x[i])/f'(x[i]) + * which will find the root in log(N) time where + * each step involves a fair bit. This is not meant to + * find huge roots [square and cube, etc]. + */ +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))) */ + + /* t3 = t1**(b-1) */ + if ((res = mp_expt_d (&t1, b - 1, &t3)) != MP_OKAY) { + goto __T3; + } + + /* numerator */ + /* t2 = t1**b */ + if ((res = mp_mul (&t3, &t1, &t2)) != MP_OKAY) { + goto __T3; + } + + /* t2 = t1**b - a */ + if ((res = mp_sub (&t2, a, &t2)) != MP_OKAY) { + goto __T3; + } + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if ((res = mp_mul_d (&t3, b, &t3)) != MP_OKAY) { + goto __T3; + } + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if ((res = mp_div (&t2, &t3, &t3, NULL)) != MP_OKAY) { + 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; +} + +/* End: bn_mp_n_root.c */ + +/* Start: bn_mp_neg.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* b = -a */ +int mp_neg (mp_int * a, mp_int * b) +{ + int res; + if ((res = mp_copy (a, b)) != MP_OKAY) { + return res; + } + if (mp_iszero(b) != MP_YES) { + b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; + } + return MP_OKAY; +} + +/* End: bn_mp_neg.c */ + +/* Start: bn_mp_or.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_or.c */ + +/* Start: bn_mp_prime_fermat.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* performs one Fermat test. + * + * If "a" were prime then b**a == b (mod a) since the order of + * the multiplicative sub-group would be phi(a) = a-1. That means + * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). + * + * Sets result to 1 if the congruence holds, or zero otherwise. + */ +int mp_prime_fermat (mp_int * a, mp_int * b, int *result) +{ + mp_int t; + int err; + + /* default to composite */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* init t */ + if ((err = mp_init (&t)) != MP_OKAY) { + return err; + } + + /* compute t = b**a mod a */ + if ((err = mp_exptmod (b, a, a, &t)) != MP_OKAY) { + goto __T; + } + + /* is it equal to b? */ + if (mp_cmp (&t, b) == MP_EQ) { + *result = MP_YES; + } + + err = MP_OKAY; +__T:mp_clear (&t); + return err; +} + +/* End: bn_mp_prime_fermat.c */ + +/* Start: bn_mp_prime_is_divisible.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines if an integers is divisible by one + * of the first PRIME_SIZE primes or not + * + * sets result to 0 if not, 1 if yes + */ +int mp_prime_is_divisible (mp_int * a, int *result) +{ + int err, ix; + mp_digit res; + + /* default to not */ + *result = MP_NO; + + for (ix = 0; ix < PRIME_SIZE; ix++) { + /* what is a mod __prime_tab[ix] */ + if ((err = mp_mod_d (a, __prime_tab[ix], &res)) != MP_OKAY) { + return err; + } + + /* is the residue zero? */ + if (res == 0) { + *result = MP_YES; + return MP_OKAY; + } + } + + return MP_OKAY; +} + +/* End: bn_mp_prime_is_divisible.c */ + +/* Start: bn_mp_prime_is_prime.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* performs a variable number of rounds of Miller-Rabin + * + * Probability of error after t rounds is no more than + * (1/4)^t when 1 <= t <= PRIME_SIZE + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime (mp_int * a, int t, int *result) +{ + mp_int b; + int ix, err, res; + + /* default to no */ + *result = MP_NO; + + /* valid value of t? */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* is the input equal to one of the primes in the table? */ + for (ix = 0; ix < PRIME_SIZE; ix++) { + if (mp_cmp_d(a, __prime_tab[ix]) == MP_EQ) { + *result = 1; + return MP_OKAY; + } + } + + /* first perform trial division */ + if ((err = mp_prime_is_divisible (a, &res)) != MP_OKAY) { + return err; + } + + /* return if it was trivially divisible */ + if (res == MP_YES) { + return MP_OKAY; + } + + /* now perform the miller-rabin rounds */ + if ((err = mp_init (&b)) != MP_OKAY) { + return err; + } + + for (ix = 0; ix < t; ix++) { + /* set the prime */ + mp_set (&b, __prime_tab[ix]); + + if ((err = mp_prime_miller_rabin (a, &b, &res)) != MP_OKAY) { + goto __B; + } + + if (res == MP_NO) { + goto __B; + } + } + + /* passed the test */ + *result = MP_YES; +__B:mp_clear (&b); + return err; +} + +/* End: bn_mp_prime_is_prime.c */ + +/* Start: bn_mp_prime_miller_rabin.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Miller-Rabin test of "a" to the base of "b" as described in + * HAC pp. 139 Algorithm 4.24 + * + * Sets result to 0 if definitely composite or 1 if probably prime. + * Randomly the chance of error is no more than 1/4 and often + * very much lower. + */ +int mp_prime_miller_rabin (mp_int * a, mp_int * b, int *result) +{ + mp_int n1, y, r; + int s, j, err; + + /* default */ + *result = MP_NO; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1) != MP_GT) { + return MP_VAL; + } + + /* get n1 = a - 1 */ + if ((err = mp_init_copy (&n1, a)) != MP_OKAY) { + return err; + } + if ((err = mp_sub_d (&n1, 1, &n1)) != MP_OKAY) { + goto __N1; + } + + /* set 2**s * r = n1 */ + if ((err = mp_init_copy (&r, &n1)) != MP_OKAY) { + goto __N1; + } + + /* count the number of least significant bits + * which are zero + */ + s = mp_cnt_lsb(&r); + + /* now divide n - 1 by 2**s */ + if ((err = mp_div_2d (&r, s, &r, NULL)) != MP_OKAY) { + goto __R; + } + + /* compute y = b**r mod a */ + if ((err = mp_init (&y)) != MP_OKAY) { + goto __R; + } + if ((err = mp_exptmod (b, &r, a, &y)) != MP_OKAY) { + goto __Y; + } + + /* if y != 1 and y != n1 do */ + if (mp_cmp_d (&y, 1) != MP_EQ && mp_cmp (&y, &n1) != MP_EQ) { + j = 1; + /* while j <= s-1 and y != n1 */ + while ((j <= (s - 1)) && mp_cmp (&y, &n1) != MP_EQ) { + if ((err = mp_sqrmod (&y, a, &y)) != MP_OKAY) { + goto __Y; + } + + /* if y == 1 then composite */ + if (mp_cmp_d (&y, 1) == MP_EQ) { + goto __Y; + } + + ++j; + } + + /* if y != n1 then composite */ + if (mp_cmp (&y, &n1) != MP_EQ) { + goto __Y; + } + } + + /* probably prime now */ + *result = MP_YES; +__Y:mp_clear (&y); +__R:mp_clear (&r); +__N1:mp_clear (&n1); + return err; +} + +/* End: bn_mp_prime_miller_rabin.c */ + +/* Start: bn_mp_prime_next_prime.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style) +{ + int err, res, x, y; + mp_digit res_tab[PRIME_SIZE], step, kstep; + mp_int b; + + /* ensure t is valid */ + if (t <= 0 || t > PRIME_SIZE) { + return MP_VAL; + } + + /* force positive */ + a->sign = MP_ZPOS; + + /* simple algo if a is less than the largest prime in the table */ + if (mp_cmp_d(a, __prime_tab[PRIME_SIZE-1]) == MP_LT) { + /* find which prime it is bigger than */ + for (x = PRIME_SIZE - 2; x >= 0; x--) { + if (mp_cmp_d(a, __prime_tab[x]) != MP_LT) { + if (bbs_style == 1) { + /* ok we found a prime smaller or + * equal [so the next is larger] + * + * however, the prime must be + * congruent to 3 mod 4 + */ + if ((__prime_tab[x + 1] & 3) != 3) { + /* scan upwards for a prime congruent to 3 mod 4 */ + for (y = x + 1; y < PRIME_SIZE; y++) { + if ((__prime_tab[y] & 3) == 3) { + mp_set(a, __prime_tab[y]); + return MP_OKAY; + } + } + } + } else { + mp_set(a, __prime_tab[x + 1]); + return MP_OKAY; + } + } + } + /* at this point a maybe 1 */ + if (mp_cmp_d(a, 1) == MP_EQ) { + mp_set(a, 2); + return MP_OKAY; + } + /* fall through to the sieve */ + } + + /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ + if (bbs_style == 1) { + kstep = 4; + } else { + kstep = 2; + } + + /* at this point we will use a combination of a sieve and Miller-Rabin */ + + if (bbs_style == 1) { + /* if a mod 4 != 3 subtract the correct value to make it so */ + if ((a->dp[0] & 3) != 3) { + if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { return err; }; + } + } else { + if (mp_iseven(a) == 1) { + /* force odd */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { + return err; + } + } + } + + /* generate the restable */ + for (x = 1; x < PRIME_SIZE; x++) { + if ((err = mp_mod_d(a, __prime_tab[x], res_tab + x)) != MP_OKAY) { + return err; + } + } + + /* init temp used for Miller-Rabin Testing */ + if ((err = mp_init(&b)) != MP_OKAY) { + return err; + } + + for (;;) { + /* skip to the next non-trivially divisible candidate */ + step = 0; + do { + /* y == 1 if any residue was zero [e.g. cannot be prime] */ + y = 0; + + /* increase step to next candidate */ + step += kstep; + + /* compute the new residue without using division */ + for (x = 1; x < PRIME_SIZE; x++) { + /* add the step to each residue */ + res_tab[x] += kstep; + + /* subtract the modulus [instead of using division] */ + if (res_tab[x] >= __prime_tab[x]) { + res_tab[x] -= __prime_tab[x]; + } + + /* set flag if zero */ + if (res_tab[x] == 0) { + y = 1; + } + } + } while (y == 1 && step < ((((mp_digit)1)<= ((((mp_digit)1)< + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ + +/* This is possibly the mother of all prime generation functions, muahahahahaha! */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) +{ + unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; + int res, err, bsize, maskOR_msb_offset; + + /* sanity check the input */ + if (size <= 1 || t <= 0) { + return MP_VAL; + } + + /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ + if (flags & LTM_PRIME_SAFE) { + flags |= LTM_PRIME_BBS; + } + + /* calc the byte size */ + bsize = (size>>3)+(size&7?1:0); + + /* we need a buffer of bsize bytes */ + tmp = OPT_CAST(unsigned char) XMALLOC(bsize); + if (tmp == NULL) { + return MP_MEM; + } + + /* calc the maskAND value for the MSbyte*/ + maskAND = 0xFF >> (8 - (size & 7)); + + /* calc the maskOR_msb */ + maskOR_msb = 0; + maskOR_msb_offset = (size - 2) >> 3; + if (flags & LTM_PRIME_2MSB_ON) { + maskOR_msb |= 1 << ((size - 2) & 7); + } else if (flags & LTM_PRIME_2MSB_OFF) { + maskAND &= ~(1 << ((size - 2) & 7)); + } + + /* get the maskOR_lsb */ + maskOR_lsb = 0; + if (flags & LTM_PRIME_BBS) { + maskOR_lsb |= 3; + } + + do { + /* read the bytes */ + if (cb(tmp, bsize, dat) != bsize) { + err = MP_VAL; + goto error; + } + + /* work over the MSbyte */ + tmp[0] &= maskAND; + tmp[0] |= 1 << ((size - 1) & 7); + + /* mix in the maskORs */ + tmp[maskOR_msb_offset] |= maskOR_msb; + tmp[bsize-1] |= maskOR_lsb; + + /* read it in */ + if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + + if (flags & LTM_PRIME_SAFE) { + /* see if (a-1)/2 is prime */ + if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { goto error; } + if ((err = mp_div_2(a, a)) != MP_OKAY) { goto error; } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } + } + } while (res == MP_NO); + + if (flags & LTM_PRIME_SAFE) { + /* restore a to the original value */ + if ((err = mp_mul_2(a, a)) != MP_OKAY) { goto error; } + if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { goto error; } + } + + err = MP_OKAY; +error: + XFREE(tmp); + return err; +} + + + +/* End: bn_mp_prime_random_ex.c */ + +/* Start: bn_mp_radix_size.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* returns size of ASCII reprensentation */ +int mp_radix_size (mp_int * a, int radix, int *size) +{ + int res, digs; + mp_int t; + mp_digit d; + + *size = 0; + + /* special case for binary */ + if (radix == 2) { + *size = mp_count_bits (a) + (a->sign == MP_NEG ? 1 : 0) + 1; + return MP_OKAY; + } + + /* make sure the radix is in range */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* init a copy of the input */ + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* digs is the digit count */ + digs = 0; + + /* if it's negative add one for the sign */ + if (t.sign == MP_NEG) { + ++digs; + t.sign = MP_ZPOS; + } + + /* fetch out all of the digits */ + while (mp_iszero (&t) == 0) { + if ((res = mp_div_d (&t, (mp_digit) radix, &t, &d)) != MP_OKAY) { + mp_clear (&t); + return res; + } + ++digs; + } + mp_clear (&t); + + /* return digs + 1, the 1 is for the NULL byte that would be required. */ + *size = digs + 1; + return MP_OKAY; +} + + +/* End: bn_mp_radix_size.c */ + +/* Start: bn_mp_radix_smap.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* chars used in radix conversions */ +const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; + +/* End: bn_mp_radix_smap.c */ + +/* Start: bn_mp_rand.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 */ + do { + d = ((mp_digit) abs (rand ())); + } while (d == 0); + + if ((res = mp_add_d (a, d, a)) != MP_OKAY) { + return res; + } + + while (digits-- > 0) { + if ((res = mp_lshd (a, 1)) != MP_OKAY) { + return res; + } + + if ((res = mp_add_d (a, ((mp_digit) abs (rand ())), a)) != MP_OKAY) { + return res; + } + } + + return MP_OKAY; +} + +/* End: bn_mp_rand.c */ + +/* Start: bn_mp_read_radix.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* 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; + + /* make sure the radix is ok */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* if the leading digit is a + * minus set the sign to negative. + */ + if (*str == '-') { + ++str; + neg = MP_NEG; + } else { + neg = MP_ZPOS; + } + + /* set the integer to the default of zero */ + mp_zero (a); + + /* process each digit of the string */ + while (*str) { + /* if the radix < 36 the conversion is case insensitive + * this allows numbers like 1AB and 1ab to represent the same value + * [e.g. in hex] + */ + ch = (char) ((radix < 36) ? toupper (*str) : *str); + for (y = 0; y < 64; y++) { + if (ch == mp_s_rmap[y]) { + break; + } + } + + /* if the char was found in the map + * and is less than the given radix add it + * to the number, otherwise exit the loop. + */ + 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; + } + + /* set the sign only if a != 0 */ + if (mp_iszero(a) != 1) { + a->sign = neg; + } + return MP_OKAY; +} + +/* End: bn_mp_read_radix.c */ + +/* Start: bn_mp_read_signed_bin.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* read magnitude */ + if ((res = mp_read_unsigned_bin (a, b + 1, c - 1)) != MP_OKAY) { + return res; + } + + /* first byte is 0 for positive, non-zero for negative */ + if (b[0] == 0) { + a->sign = MP_ZPOS; + } else { + a->sign = MP_NEG; + } + + return MP_OKAY; +} + +/* End: bn_mp_read_signed_bin.c */ + +/* Start: bn_mp_read_unsigned_bin.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* make sure there are at least two digits */ + if (a->alloc < 2) { + if ((res = mp_grow(a, 2)) != MP_OKAY) { + return res; + } + } + + /* zero the int */ + mp_zero (a); + + /* read the bytes in */ + while (c-- > 0) { + if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { + return res; + } + +#ifndef MP_8BIT + a->dp[0] |= *b++; + a->used += 1; +#else + a->dp[0] = (*b & MP_MASK); + a->dp[1] |= ((*b++ >> 7U) & 1); + a->used += 2; +#endif + } + mp_clamp (a); + return MP_OKAY; +} + +/* End: bn_mp_read_unsigned_bin.c */ + +/* Start: bn_mp_reduce.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* 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; + + /* q = x */ + if ((res = mp_init_copy (&q, x)) != MP_OKAY) { + return res; + } + + /* q1 = x / b**(k-1) */ + mp_rshd (&q, um - 1); + + /* according to HAC this optimization is ok */ + if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { + 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; + } + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd (&q, um + 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) { + goto CLEANUP; + } + } + +CLEANUP: + mp_clear (&q); + + return res; +} + +/* End: bn_mp_reduce.c */ + +/* Start: bn_mp_reduce_2k.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* reduces a modulo n where n is of the form 2**p - d */ +int +mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) +{ + mp_int q; + int p, res; + + if ((res = mp_init(&q)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(n); +top: + /* q = a/2**p, a = a mod 2**p */ + if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (d != 1) { + /* q = q * d */ + if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { + goto ERR; + } + } + + /* a = a + q */ + if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { + goto ERR; + } + + if (mp_cmp_mag(a, n) != MP_LT) { + s_mp_sub(a, n, a); + goto top; + } + +ERR: + mp_clear(&q); + return res; +} + + +/* End: bn_mp_reduce_2k.c */ + +/* Start: bn_mp_reduce_2k_setup.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines the setup value */ +int +mp_reduce_2k_setup(mp_int *a, mp_digit *d) +{ + int res, p; + mp_int tmp; + + if ((res = mp_init(&tmp)) != MP_OKAY) { + return res; + } + + p = mp_count_bits(a); + if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + *d = tmp.dp[0]; + mp_clear(&tmp); + return MP_OKAY; +} + +/* End: bn_mp_reduce_2k_setup.c */ + +/* Start: bn_mp_reduce_is_2k.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* determines if mp_reduce_2k can be used */ +int mp_reduce_is_2k(mp_int *a) +{ + int ix, iy, iz, iw; + + if (a->used == 0) { + return 0; + } else if (a->used == 1) { + return 1; + } else if (a->used > 1) { + iy = mp_count_bits(a); + iz = 1; + iw = 1; + + /* Test every bit from the second digit up, must be 1 */ + for (ix = DIGIT_BIT; ix < iy; ix++) { + if ((a->dp[iw] & iz) == 0) { + return 0; + } + iz <<= 1; + if (iz > (int)MP_MASK) { + ++iw; + iz = 1; + } + } + } + return 1; +} + + +/* End: bn_mp_reduce_is_2k.c */ + +/* Start: bn_mp_reduce_setup.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + } + return mp_div (a, b, a, NULL); +} + +/* End: bn_mp_reduce_setup.c */ + +/* Start: bn_mp_rshd.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + } + + { + register mp_digit *bottom, *top; + + /* shift the digits down */ + + /* bottom */ + bottom = a->dp; + + /* top [offset into digits] */ + top = a->dp + b; + + /* this is implemented as a sliding window where + * the window is b-digits long and digits from + * the top of the window are copied to the bottom + * + * e.g. + + b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> + /\ | ----> + \-------------------/ ----> + */ + for (x = 0; x < (a->used - b); x++) { + *bottom++ = *top++; + } + + /* zero the top digits */ + for (; x < a->used; x++) { + *bottom++ = 0; + } + } + + /* remove excess digits */ + a->used -= b; +} + +/* End: bn_mp_rshd.c */ + +/* Start: bn_mp_set.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_set.c */ + +/* Start: bn_mp_set_int.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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 */ + 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 += 1; + } + mp_clamp (a); + return MP_OKAY; +} + +/* End: bn_mp_set_int.c */ + +/* Start: bn_mp_shrink.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* shrink a bignum */ +int mp_shrink (mp_int * a) +{ + mp_digit *tmp; + if (a->alloc != a->used && a->used > 0) { + if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * a->used)) == NULL) { + return MP_MEM; + } + a->dp = tmp; + a->alloc = a->used; + } + return MP_OKAY; +} + +/* End: bn_mp_shrink.c */ + +/* Start: bn_mp_signed_bin_size.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* get the size for an signed equivalent */ +int mp_signed_bin_size (mp_int * a) +{ + return 1 + mp_unsigned_bin_size (a); +} + +/* End: bn_mp_signed_bin_size.c */ + +/* Start: bn_mp_sqr.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* computes b = a*a */ +int +mp_sqr (mp_int * a, mp_int * b) +{ + int res; + + /* use Toom-Cook? */ + if (a->used >= TOOM_SQR_CUTOFF) { + res = mp_toom_sqr(a, b); + /* Karatsuba? */ + } else if (a->used >= KARATSUBA_SQR_CUTOFF) { + res = mp_karatsuba_sqr (a, b); + } else { + /* can we use the fast comba multiplier? */ + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < + (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { + res = fast_s_mp_sqr (a, b); + } else { + res = s_mp_sqr (a, b); + } + } + b->sign = MP_ZPOS; + return res; +} + +/* End: bn_mp_sqr.c */ + +/* Start: bn_mp_sqrmod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_sqrmod.c */ + +/* Start: bn_mp_sqrt.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* this function is less generic than mp_n_root, simpler and faster */ +int mp_sqrt(mp_int *arg, mp_int *ret) +{ + int res; + mp_int t1,t2; + + /* must be positive */ + if (arg->sign == MP_NEG) { + return MP_VAL; + } + + /* easy out */ + if (mp_iszero(arg) == MP_YES) { + mp_zero(ret); + return MP_OKAY; + } + + if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { + return res; + } + + if ((res = mp_init(&t2)) != MP_OKAY) { + goto E2; + } + + /* First approx. (not very bad for large arg) */ + mp_rshd (&t1,t1.used/2); + + /* t1 > 0 */ + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* And now t1 > sqrt(arg) */ + do { + if ((res = mp_div(arg,&t1,&t2,NULL)) != MP_OKAY) { + goto E1; + } + if ((res = mp_add(&t1,&t2,&t1)) != MP_OKAY) { + goto E1; + } + if ((res = mp_div_2(&t1,&t1)) != MP_OKAY) { + goto E1; + } + /* t1 >= sqrt(arg) >= t2 at this point */ + } while (mp_cmp_mag(&t1,&t2) == MP_GT); + + mp_exch(&t1,ret); + +E1: mp_clear(&t2); +E2: mp_clear(&t1); + return res; +} + + +/* End: bn_mp_sqrt.c */ + +/* Start: bn_mp_sub.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + if (sa != sb) { + /* subtract a negative from a positive, OR */ + /* subtract a positive from a negative. */ + /* In either case, ADD their magnitudes, */ + /* and use the sign of the first number. */ + c->sign = sa; + res = s_mp_add (a, b, c); + } else { + /* subtract a positive from a positive, OR */ + /* subtract a negative from a negative. */ + /* First, take the difference between their */ + /* magnitudes, then... */ + if (mp_cmp_mag (a, b) != MP_LT) { + /* Copy the sign from the first */ + c->sign = sa; + /* The first has a larger or equal magnitude */ + res = s_mp_sub (a, b, c); + } else { + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; + /* The second has a larger magnitude */ + res = s_mp_sub (b, a, c); + } + } + return res; +} + + +/* End: bn_mp_sub.c */ + +/* Start: bn_mp_sub_d.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* single digit subtraction */ +int +mp_sub_d (mp_int * a, mp_digit b, mp_int * c) +{ + mp_digit *tmpa, *tmpc, mu; + int res, ix, oldused; + + /* grow c as required */ + if (c->alloc < a->used + 1) { + if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { + return res; + } + } + + /* if a is negative just do an unsigned + * addition [with fudged signs] + */ + if (a->sign == MP_NEG) { + a->sign = MP_ZPOS; + res = mp_add_d(a, b, c); + a->sign = c->sign = MP_NEG; + return res; + } + + /* setup regs */ + oldused = c->used; + tmpa = a->dp; + tmpc = c->dp; + + /* if a <= b simply fix the single digit */ + if ((a->used == 1 && a->dp[0] <= b) || a->used == 0) { + if (a->used == 1) { + *tmpc++ = b - *tmpa; + } else { + *tmpc++ = b; + } + ix = 1; + + /* negative/1digit */ + c->sign = MP_NEG; + c->used = 1; + } else { + /* positive/size */ + c->sign = MP_ZPOS; + c->used = a->used; + + /* subtract first digit */ + *tmpc = *tmpa++ - b; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + + /* handle rest of the digits */ + for (ix = 1; ix < a->used; ix++) { + *tmpc = *tmpa++ - mu; + mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1); + *tmpc++ &= MP_MASK; + } + } + + /* zero excess digits */ + while (ix++ < oldused) { + *tmpc++ = 0; + } + mp_clamp(c); + return MP_OKAY; +} + + +/* End: bn_mp_sub_d.c */ + +/* Start: bn_mp_submod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_submod.c */ + +/* Start: bn_mp_to_signed_bin.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_to_signed_bin.c */ + +/* Start: bn_mp_to_unsigned_bin.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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) { +#ifndef MP_8BIT + b[x++] = (unsigned char) (t.dp[0] & 255); +#else + b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); +#endif + 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; +} + +/* End: bn_mp_to_unsigned_bin.c */ + +/* Start: bn_mp_toom_mul.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* multiplication using the Toom-Cook 3-way algorithm */ +int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = MIN(a->used, b->used) / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* b = b2 * B**2 + b1 * B + b0 */ + if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(b, &b1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b1, B); + mp_mod_2d(&b1, DIGIT_BIT * B, &b1); + + if ((res = mp_copy(b, &b2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&b2, B*2); + + /* w0 = a0*b0 */ + if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * b2 */ + if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, + 2 small divisions and 1 small multiplication + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, + &a0, &a1, &a2, &b0, &b1, + &b2, &tmp1, &tmp2, NULL); + return res; +} + + +/* End: bn_mp_toom_mul.c */ + +/* Start: bn_mp_toom_sqr.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* squaring using Toom-Cook 3-way algorithm */ +int +mp_toom_sqr(mp_int *a, mp_int *b) +{ + mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; + int res, B; + + /* init temps */ + if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { + return res; + } + + /* B */ + B = a->used / 3; + + /* a = a2 * B**2 + a1 * B + a0 */ + if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_copy(a, &a1)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a1, B); + mp_mod_2d(&a1, DIGIT_BIT * B, &a1); + + if ((res = mp_copy(a, &a2)) != MP_OKAY) { + goto ERR; + } + mp_rshd(&a2, B*2); + + /* w0 = a0*a0 */ + if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { + goto ERR; + } + + /* w4 = a2 * a2 */ + if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { + goto ERR; + } + + /* w1 = (a2 + 2(a1 + 2a0))**2 */ + if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + + /* w3 = (a0 + 2(a1 + 2a2))**2 */ + if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + + + /* w2 = (a2 + a1 + a0)**2 */ + if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { + goto ERR; + } + + /* now solve the matrix + + 0 0 0 0 1 + 1 2 4 8 16 + 1 1 1 1 1 + 16 8 4 2 1 + 1 0 0 0 0 + + using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. + */ + + /* r1 - r4 */ + if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r0 */ + if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/2 */ + if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3/2 */ + if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { + goto ERR; + } + /* r2 - r0 - r4 */ + if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1 - 8r0 */ + if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - 8r4 */ + if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { + goto ERR; + } + /* 3r2 - r1 - r3 */ + if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { + goto ERR; + } + /* r1 - r2 */ + if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { + goto ERR; + } + /* r3 - r2 */ + if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { + goto ERR; + } + /* r1/3 */ + if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { + goto ERR; + } + /* r3/3 */ + if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { + goto ERR; + } + + /* at this point shift W[n] by B*n */ + if ((res = mp_lshd(&w1, 1*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w2, 2*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w3, 3*B)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_lshd(&w4, 4*B)) != MP_OKAY) { + goto ERR; + } + + if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { + goto ERR; + } + if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { + goto ERR; + } + +ERR: + mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); + return res; +} + + +/* End: bn_mp_toom_sqr.c */ + +/* Start: bn_mp_toradix.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* 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; + + /* check range of the radix */ + if (radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == 1) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + 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++ = mp_s_rmap[d]; + ++digs; + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear (&t); + return MP_OKAY; +} + + +/* End: bn_mp_toradix.c */ + +/* Start: bn_mp_toradix_n.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* stores a bignum as a ASCII string in a given radix (2..64) + * + * Stores upto maxlen-1 chars and always a NULL byte + */ +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen) +{ + int res, digs; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of the maxlen, radix */ + if (maxlen < 3 || radix < 2 || radix > 64) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a) == 1) { + *str++ = '0'; + *str = '\0'; + return MP_OKAY; + } + + if ((res = mp_init_copy (&t, a)) != MP_OKAY) { + return res; + } + + /* if it is negative output a - */ + if (t.sign == MP_NEG) { + /* we have to reverse our digits later... but not the - sign!! */ + ++_s; + + /* store the flag and mark the number as positive */ + *str++ = '-'; + t.sign = MP_ZPOS; + + /* subtract a char */ + --maxlen; + } + + 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++ = mp_s_rmap[d]; + ++digs; + + if (--maxlen == 1) { + /* no more room */ + break; + } + } + + /* reverse the digits of the string. In this case _s points + * to the first digit [exluding the sign] of the number] + */ + bn_reverse ((unsigned char *)_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + + mp_clear (&t); + return MP_OKAY; +} + + +/* End: bn_mp_toradix_n.c */ + +/* Start: bn_mp_unsigned_bin_size.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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)); +} + +/* End: bn_mp_unsigned_bin_size.c */ + +/* Start: bn_mp_xor.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; +} + +/* End: bn_mp_xor.c */ + +/* Start: bn_mp_zero.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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); +} + +/* End: bn_mp_zero.c */ + +/* Start: bn_prime_sizes_tab.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* this table gives the # of rabin miller trials for a prob of failure lower than 2^-96 */ +static const struct { + int k, t; +} sizes[] = { +{ 128, 28 }, +{ 256, 16 }, +{ 384, 10 }, +{ 512, 7 }, +{ 640, 6 }, +{ 768, 5 }, +{ 896, 4 }, +{ 1024, 4 }, +{ 1152, 3 }, +{ 1280, 3 }, +{ 1408, 3 }, +{ 1536, 3 }, +{ 1664, 3 }, +{ 1792, 2 } }; + +/* returns # of RM trials required for a given bit size */ +int mp_prime_rabin_miller_trials(int size) +{ + int x; + + for (x = 0; x < (int)(sizeof(sizes)/(sizeof(sizes[0]))); x++) { + if (sizes[x].k == size) { + return sizes[x].t; + } else if (sizes[x].k > size) { + return (x == 0) ? sizes[0].t : sizes[x - 1].t; + } + } + return 1; +} + + + +/* End: bn_prime_sizes_tab.c */ + +/* Start: bn_prime_tab.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include +const mp_digit __prime_tab[] = { + 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, + 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, + 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, + 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, +#ifndef MP_8BIT + 0x0083, + 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, + 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, + 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, + 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, + + 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, + 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, + 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, + 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, + 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, + 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, + 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, + 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, + + 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, + 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, + 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, + 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, + 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, + 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, + 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, + 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, + + 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, + 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, + 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, + 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, + 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, + 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, + 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, + 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 +#endif +}; + +/* End: bn_prime_tab.c */ + +/* Start: bn_reverse.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + } +} + +/* End: bn_reverse.c */ + +/* Start: bn_s_mp_add.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* 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 { + min = a->used; + max = b->used; + x = b; + } + + /* init result */ + if (c->alloc < max + 1) { + if ((res = mp_grow (c, max + 1)) != MP_OKAY) { + return res; + } + } + + /* get old used digit count and set new one */ + olduse = c->used; + c->used = max + 1; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + + /* first input */ + tmpa = a->dp; + + /* second input */ + tmpb = b->dp; + + /* destination */ + tmpc = c->dp; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + *tmpc = *tmpa++ + *tmpb++ + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= 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 */ + *tmpc = x->dp[i] + u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)DIGIT_BIT); + + /* take away carry bit from T[i] */ + *tmpc++ &= MP_MASK; + } + } + + /* add carry */ + *tmpc++ = u; + + /* clear digits above oldused */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + +/* End: bn_s_mp_add.c */ + +/* Start: bn_s_mp_exptmod.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +#ifdef MP_LOW_MEM + #define TAB_SIZE 32 +#else + #define TAB_SIZE 256 +#endif + +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + 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; + } + +#ifdef MP_LOW_MEM + if (winsize > 5) { + winsize = 5; + } +#endif + + /* init M array */ + /* init first cell */ + if ((err = mp_init(&M[1])) != MP_OKAY) { + return err; + } + + /* now init the second half of the array */ + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + if ((err = mp_init(&M[x])) != MP_OKAY) { + for (y = 1<<(winsize-1); y < x; y++) { + mp_clear (&M[y]); + } + mp_clear(&M[1]); + 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 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, that is M[x] = M[x-1] * M[1] (mod P) + * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) + */ + 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 = 1; + buf = 0; + digidx = X->used - 1; + bitcpy = 0; + bitbuf = 0; + + for (;;) { + /* grab next digit as required */ + if (--bitcnt == 0) { + /* if digidx == -1 we are out of digits */ + if (digidx == -1) { + break; + } + /* read next digit and reset the bitcnt */ + buf = X->dp[digidx--]; + bitcnt = (int) DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; + buf <<= (mp_digit)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 */ + /* 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 __RES; + } + if ((err = mp_reduce (&res, P, &mu)) != MP_OKAY) { + goto __RES; + } + + /* empty window and reset */ + bitcpy = 0; + 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: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear (&M[x]); + } + return err; +} + +/* End: bn_s_mp_exptmod.c */ + +/* Start: bn_s_mp_mul_digs.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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? */ + if (((digs) < MP_WARRAY) && + MIN (a->used, b->used) < + (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_digs (a, b, c, digs); + } + + 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 */ + /* copy of the digit from a used within the nested loop */ + tmpx = a->dp[ix]; + + /* an alias for the destination shifted ix places */ + tmpt = t.dp + ix; + + /* an alias for the digits of b */ + 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)); + } + /* set carry if it is placed below digs */ + if (ix + iy < digs) { + *tmpt = u; + } + } + + mp_clamp (&t); + mp_exch (&t, c); + + mp_clear (&t); + return MP_OKAY; +} + +/* End: bn_s_mp_mul_digs.c */ + +/* Start: bn_s_mp_mul_high_digs.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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) < MP_WARRAY) + && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { + return fast_s_mp_mul_high_digs (a, b, c, digs); + } + + 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; +} + +/* End: bn_s_mp_mul_high_digs.c */ + +/* Start: bn_s_mp_sqr.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + mp_digit u, tmpx, *tmpt; + + pa = a->used; + if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { + return res; + } + + /* default used is maximum possible size */ + t.used = 2*pa + 1; + + for (ix = 0; ix < pa; ix++) { + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + r = ((mp_word) t.dp[2*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 = (mp_digit)(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 + (2*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 it's 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 = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + /* propagate upwards */ + while (u != ((mp_digit) 0)) { + r = ((mp_word) *tmpt) + ((mp_word) u); + *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); + u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); + } + } + + mp_clamp (&t); + mp_exch (&t, b); + mp_clear (&t); + return MP_OKAY; +} + +/* End: bn_s_mp_sqr.c */ + +/* Start: bn_s_mp_sub.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#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; + + /* 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; + + { + register mp_digit u, *tmpa, *tmpb, *tmpc; + register int i; + + /* alias for digit pointers */ + tmpa = a->dp; + tmpb = b->dp; + tmpc = c->dp; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + *tmpc = *tmpa++ - *tmpb++ - u; + + /* U = carry bit of T[i] + * Note this saves performing an AND operation since + * if a carry does occur it will propagate all the way to the + * MSB. As a result a single shift is enough to get the carry + */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + *tmpc = *tmpa++ - u; + + /* U = carry bit of T[i] */ + u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); + + /* Clear carry from T[i] */ + *tmpc++ &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + for (i = c->used; i < olduse; i++) { + *tmpc++ = 0; + } + } + + mp_clamp (c); + return MP_OKAY; +} + + +/* End: bn_s_mp_sub.c */ + +/* Start: bncore.c */ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#include + +/* Known optimal configurations + + CPU /Compiler /MUL CUTOFF/SQR CUTOFF +------------------------------------------------------------- + Intel P4 /GCC v3.2 / 70/ 108 + AMD Athlon XP /GCC v3.2 / 109/ 127 + +*/ + +/* configured for a AMD XP Thoroughbred core with etc/tune.c */ +int KARATSUBA_MUL_CUTOFF = 109, /* Min. number of digits before Karatsuba multiplication is used. */ + KARATSUBA_SQR_CUTOFF = 127, /* Min. number of digits before Karatsuba squaring is used. */ + + TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ + TOOM_SQR_CUTOFF = 400; + +/* End: bncore.c */ + + +/* EOF */ diff --git a/tommath.h b/tommath.h new file mode 100644 index 0000000..0029994 --- /dev/null +++ b/tommath.h @@ -0,0 +1,556 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis + * + * LibTomMath is a library that provides multiple-precision + * integer arithmetic as well as number theoretic functionality. + * + * The library was 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://math.libtomcrypt.org + */ +#ifndef BN_H_ +#define BN_H_ + +#include +#include +#include +#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" { + +/* C++ compilers don't like assigning void * to mp_digit * */ +#define OPT_CAST(x) (x *) + +#else + +/* C on the other hand doesn't care */ +#define OPT_CAST(x) + +#endif + +/* some default configurations. + * + * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits + * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits + * + * At the very least a mp_digit must be able to hold 7 bits + * [any size beyond that is ok provided it doesn't overflow the data type] + */ +#ifdef MP_8BIT + typedef unsigned char mp_digit; + typedef unsigned short mp_word; +#elif defined(MP_16BIT) + typedef unsigned short mp_digit; + typedef unsigned long mp_word; +#elif defined(MP_64BIT) + /* for GCC only on supported platforms */ +#ifndef CRYPT + typedef unsigned long long ulong64; + typedef signed long long long64; +#endif + + typedef ulong64 mp_digit; + typedef unsigned long mp_word __attribute__ ((mode(TI))); + + #define DIGIT_BIT 60 +#else + /* this is the default case, 28-bit digits */ + + /* this is to make porting into LibTomCrypt easier :-) */ +#ifndef CRYPT + #if defined(_MSC_VER) || defined(__BORLANDC__) + typedef unsigned __int64 ulong64; + typedef signed __int64 long64; + #else + typedef unsigned long long ulong64; + typedef signed long long long64; + #endif +#endif + + typedef unsigned long mp_digit; + typedef ulong64 mp_word; + +#ifdef MP_31BIT + /* this is an extension that uses 31-bit digits */ + #define DIGIT_BIT 31 +#else + /* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ + #define DIGIT_BIT 28 + #define MP_28BIT +#endif +#endif + +/* define heap macros */ +#ifndef CRYPT + /* default to libc stuff */ + #ifndef XMALLOC + #define XMALLOC malloc + #define XFREE free + #define XREALLOC realloc + #define XCALLOC calloc + #else + /* prototypes for our heap functions */ + extern void *XMALLOC(size_t n); + extern void *REALLOC(void *p, size_t n); + extern void *XCALLOC(size_t n, size_t s); + extern void XFREE(void *p); + #endif +#endif + + +/* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ +#ifndef DIGIT_BIT + #define DIGIT_BIT ((int)((CHAR_BIT * sizeof(mp_digit) - 1))) /* bits per digit */ +#endif + +#define MP_DIGIT_BIT DIGIT_BIT +#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) +#define MP_DIGIT_MAX MP_MASK + +/* equalities */ +#define MP_LT -1 /* less than */ +#define MP_EQ 0 /* equal to */ +#define MP_GT 1 /* greater than */ + +#define MP_ZPOS 0 /* positive integer */ +#define MP_NEG 1 /* negative */ + +#define MP_OKAY 0 /* ok result */ +#define MP_MEM -2 /* out of mem */ +#define MP_VAL -3 /* invalid input */ +#define MP_RANGE MP_VAL + +#define MP_YES 1 /* yes response */ +#define MP_NO 0 /* no response */ + +/* Primality generation flags */ +#define LTM_PRIME_BBS 0x0001 /* BBS style prime */ +#define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ +#define LTM_PRIME_2MSB_OFF 0x0004 /* force 2nd MSB to 0 */ +#define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ + +typedef int mp_err; + +/* you'll have to tune these... */ +extern int KARATSUBA_MUL_CUTOFF, + KARATSUBA_SQR_CUTOFF, + TOOM_MUL_CUTOFF, + TOOM_SQR_CUTOFF; + +/* define this to use lower memory usage routines (exptmods mostly) */ +/* #define MP_LOW_MEM */ + +/* default precision */ +#ifndef MP_PREC + #ifdef MP_LOW_MEM + #define MP_PREC 64 /* default digits of precision */ + #else + #define MP_PREC 8 /* default digits of precision */ + #endif +#endif + +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ +#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc, sign; + mp_digit *dp; +} mp_int; + +/* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ +typedef int ltm_prime_callback(unsigned char *dst, int len, void *dat); + + +#define USED(m) ((m)->used) +#define DIGIT(m,k) ((m)->dp[(k)]) +#define SIGN(m) ((m)->sign) + +/* error code to char* string */ +char *mp_error_to_string(int code); + +/* ---> init and deinit bignum functions <--- */ +/* init a bignum */ +int mp_init(mp_int *a); + +/* free a bignum */ +void mp_clear(mp_int *a); + +/* init a null terminated series of arguments */ +int mp_init_multi(mp_int *mp, ...); + +/* clear a null terminated series of arguments */ +void mp_clear_multi(mp_int *mp, ...); + +/* exchange two ints */ +void mp_exch(mp_int *a, mp_int *b); + +/* shrink ram required for a bignum */ +int mp_shrink(mp_int *a); + +/* 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); + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) +#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) + +/* set to zero */ +void mp_zero(mp_int *a); + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b); + +/* set a 32-bit const */ +int mp_set_int(mp_int *a, unsigned long b); + +/* get a 32-bit value */ +unsigned long mp_get_int(mp_int * a); + +/* initialize and set a digit */ +int mp_init_set (mp_int * a, mp_digit b); + +/* initialize and set 32-bit value */ +int mp_init_set_int (mp_int * a, unsigned long b); + +/* copy, b = a */ +int mp_copy(mp_int *a, mp_int *b); + +/* inits and copies, a = b */ +int mp_init_copy(mp_int *a, mp_int *b); + +/* trim unused digits */ +void mp_clamp(mp_int *a); + +/* ---> digit manipulation <--- */ + +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +int mp_lshd(mp_int *a, int b); + +/* c = a / 2**b */ +int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); + +/* b = a/2 */ +int mp_div_2(mp_int *a, mp_int *b); + +/* c = a * 2**b */ +int mp_mul_2d(mp_int *a, int b, mp_int *c); + +/* b = a*2 */ +int mp_mul_2(mp_int *a, mp_int *b); + +/* c = a mod 2**d */ +int mp_mod_2d(mp_int *a, int b, mp_int *c); + +/* computes a = 2**b */ +int mp_2expt(mp_int *a, int b); + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(mp_int *a); + +/* I Love Earth! */ + +/* 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 */ +int mp_neg(mp_int *a, mp_int *b); + +/* b = |a| */ +int mp_abs(mp_int *a, mp_int *b); + +/* compare a to b */ +int mp_cmp(mp_int *a, mp_int *b); + +/* compare |a| to |b| */ +int mp_cmp_mag(mp_int *a, mp_int *b); + +/* c = a + b */ +int mp_add(mp_int *a, mp_int *b, mp_int *c); + +/* c = a - b */ +int mp_sub(mp_int *a, mp_int *b, mp_int *c); + +/* c = a * b */ +int mp_mul(mp_int *a, mp_int *b, mp_int *c); + +/* b = a*a */ +int mp_sqr(mp_int *a, mp_int *b); + +/* a/b => cb + d == a */ +int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a mod b, 0 <= c < b */ +int mp_mod(mp_int *a, mp_int *b, mp_int *c); + +/* ---> single digit functions <--- */ + +/* compare against a single digit */ +int mp_cmp_d(mp_int *a, mp_digit b); + +/* c = a + b */ +int mp_add_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a - b */ +int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a * b */ +int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); + +/* a/b => cb + d == a */ +int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); + +/* a/3 => 3c + d == a */ +int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); + +/* c = a**b */ +int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); + +/* c = a mod b, 0 <= c < b */ +int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); + +/* ---> number theory <--- */ + +/* d = a + b (mod c) */ +int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a - b (mod c) */ +int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* d = a * b (mod c) */ +int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* c = a * a (mod b) */ +int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = 1/a (mod b) */ +int mp_invmod(mp_int *a, mp_int *b, mp_int *c); + +/* c = (a, b) */ +int mp_gcd(mp_int *a, mp_int *b, mp_int *c); + +/* produces value such that U1*a + U2*b = U3 */ +int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); + +/* c = [a, b] or (a*b)/(a, b) */ +int mp_lcm(mp_int *a, mp_int *b, mp_int *c); + +/* finds one of the b'th root of a, such that |c|**b <= |a| + * + * returns error if a < 0 and b is even + */ +int mp_n_root(mp_int *a, mp_digit b, mp_int *c); + +/* special sqrt algo */ +int mp_sqrt(mp_int *arg, mp_int *ret); + +/* is number a square? */ +int mp_is_square(mp_int *arg, int *ret); + +/* computes the jacobi c = (a | n) (or Legendre if b is prime) */ +int mp_jacobi(mp_int *a, mp_int *n, int *c); + +/* used to setup the Barrett reduction for a given modulus b */ +int mp_reduce_setup(mp_int *a, mp_int *b); + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. + */ +int mp_reduce(mp_int *a, mp_int *b, mp_int *c); + +/* setups the montgomery reduction */ +int mp_montgomery_setup(mp_int *a, mp_digit *mp); + +/* computes a = B**n mod b without division or multiplication useful for + * normalizing numbers in a Montgomery system. + */ +int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); + +/* computes x/R == x (mod N) via Montgomery Reduction */ +int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); + +/* returns 1 if a is a valid DR modulus */ +int mp_dr_is_modulus(mp_int *a); + +/* sets the value of "d" required for mp_dr_reduce */ +void mp_dr_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b using the Diminished Radix method */ +int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); + +/* returns true if a can be reduced with mp_reduce_2k */ +int mp_reduce_is_2k(mp_int *a); + +/* determines k value for 2k reduction */ +int mp_reduce_2k_setup(mp_int *a, mp_digit *d); + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); + +/* d = a**b (mod c) */ +int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); + +/* ---> Primes <--- */ + +/* number of primes */ +#ifdef MP_8BIT + #define PRIME_SIZE 31 +#else + #define PRIME_SIZE 256 +#endif + +/* table of first PRIME_SIZE primes */ +extern const mp_digit __prime_tab[]; + +/* result=1 if a is divisible by one of the first PRIME_SIZE primes */ +int mp_prime_is_divisible(mp_int *a, int *result); + +/* performs one Fermat test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_fermat(mp_int *a, mp_int *b, int *result); + +/* performs one Miller-Rabin test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); + +/* This gives [for a given bit size] the number of trials required + * such that Miller-Rabin gives a prob of failure lower than 2^-96 + */ +int mp_prime_rabin_miller_trials(int size); + +/* performs t rounds of Miller-Rabin on "a" using the first + * t prime bases. Also performs an initial sieve of trial + * division. Determines if "a" is prime with probability + * of error no more than (1/4)**t. + * + * Sets result to 1 if probably prime, 0 otherwise + */ +int mp_prime_is_prime(mp_int *a, int t, int *result); + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = 1 means the prime must be congruent to 3 mod 4 + */ +int mp_prime_next_prime(mp_int *a, int t, int bbs_style); + +/* makes a truly random prime of a given size (bytes), + * call with bbs = 1 if you want it to be congruent to 3 mod 4 + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + * The prime generated will be larger than 2^(8*size). + */ +#define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs==1)?LTM_PRIME_BBS:0, cb, dat) + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * LTM_PRIME_BBS - make prime congruent to 3 mod 4 + * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) + * LTM_PRIME_2MSB_OFF - make the 2nd highest bit zero + * LTM_PRIME_2MSB_ON - make the 2nd highest bit one + * + * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can + * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself + * so it can be NULL + * + */ +int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); + +/* ---> radix conversion <--- */ +int mp_count_bits(mp_int *a); + +int mp_unsigned_bin_size(mp_int *a); +int mp_read_unsigned_bin(mp_int *a, unsigned char *b, int c); +int mp_to_unsigned_bin(mp_int *a, unsigned char *b); + +int mp_signed_bin_size(mp_int *a); +int mp_read_signed_bin(mp_int *a, unsigned char *b, int c); +int mp_to_signed_bin(mp_int *a, unsigned char *b); + +int mp_read_radix(mp_int *a, char *str, int radix); +int mp_toradix(mp_int *a, char *str, int radix); +int mp_toradix_n(mp_int * a, char *str, int radix, int maxlen); +int mp_radix_size(mp_int *a, int radix, int *size); + +int mp_fread(mp_int *a, int radix, FILE *stream); +int mp_fwrite(mp_int *a, int radix, FILE *stream); + +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp) mp_signed_bin_size(mp) +#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp) mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#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_toom_mul(mp_int *a, mp_int *b, mp_int *c); +int mp_karatsuba_sqr(mp_int *a, mp_int *b); +int mp_toom_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, int mode); +int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y); +void bn_reverse(unsigned char *s, int len); + +extern const char *mp_s_rmap; + +#ifdef __cplusplus + } +#endif + +#endif +