From 460e14e5860a503b8e7716ce18a29eb99f517cf7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 11 Jul 2021 16:08:16 +0100 Subject: [PATCH] Change the compiler's big integer library to use libTomMath This now replaces Bill's crappy big int implementation --- Makefile | 9 +- build.bat | 15 +- src/big_int.cpp | 1211 ++-------------- src/check_builtin.cpp | 26 +- src/check_expr.cpp | 20 +- src/check_type.cpp | 8 +- src/exact_value.cpp | 5 +- src/libtommath/LICENSE | 26 + src/libtommath/README.md | 44 + src/libtommath/mp_2expt.c | 31 + src/libtommath/mp_abs.c | 24 + src/libtommath/mp_add.c | 29 + src/libtommath/mp_add_d.c | 86 ++ src/libtommath/mp_addmod.c | 15 + src/libtommath/mp_and.c | 54 + src/libtommath/mp_clamp.c | 27 + src/libtommath/mp_clear.c | 20 + src/libtommath/mp_clear_multi.c | 18 + src/libtommath/mp_cmp.c | 21 + src/libtommath/mp_cmp_d.c | 26 + src/libtommath/mp_cmp_mag.c | 25 + src/libtommath/mp_cnt_lsb.c | 38 + src/libtommath/mp_complement.c | 13 + src/libtommath/mp_copy.c | 29 + src/libtommath/mp_count_bits.c | 28 + src/libtommath/mp_cutoffs.c | 14 + src/libtommath/mp_div.c | 42 + src/libtommath/mp_div_2.c | 40 + src/libtommath/mp_div_2d.c | 61 + src/libtommath/mp_div_d.c | 84 ++ src/libtommath/mp_dr_is_modulus.c | 27 + src/libtommath/mp_dr_reduce.c | 68 + src/libtommath/mp_dr_setup.c | 15 + src/libtommath/mp_error_to_string.c | 29 + src/libtommath/mp_exch.c | 13 + src/libtommath/mp_expt_n.c | 43 + src/libtommath/mp_exptmod.c | 78 + src/libtommath/mp_exteuclid.c | 72 + src/libtommath/mp_fread.c | 66 + src/libtommath/mp_from_sbin.c | 21 + src/libtommath/mp_from_ubin.c | 30 + src/libtommath/mp_fwrite.c | 33 + src/libtommath/mp_gcd.c | 92 ++ src/libtommath/mp_get_double.c | 18 + src/libtommath/mp_get_i32.c | 7 + src/libtommath/mp_get_i64.c | 7 + src/libtommath/mp_get_l.c | 7 + src/libtommath/mp_get_mag_u32.c | 7 + src/libtommath/mp_get_mag_u64.c | 7 + src/libtommath/mp_get_mag_ul.c | 7 + src/libtommath/mp_grow.c | 40 + src/libtommath/mp_init.c | 23 + src/libtommath/mp_init_copy.c | 21 + src/libtommath/mp_init_i32.c | 7 + src/libtommath/mp_init_i64.c | 7 + src/libtommath/mp_init_l.c | 7 + src/libtommath/mp_init_multi.c | 41 + src/libtommath/mp_init_set.c | 16 + src/libtommath/mp_init_size.c | 28 + src/libtommath/mp_init_u32.c | 7 + src/libtommath/mp_init_u64.c | 7 + src/libtommath/mp_init_ul.c | 7 + src/libtommath/mp_invmod.c | 29 + src/libtommath/mp_is_square.c | 93 ++ src/libtommath/mp_kronecker.c | 129 ++ src/libtommath/mp_lcm.c | 44 + src/libtommath/mp_log_n.c | 29 + src/libtommath/mp_lshd.c | 42 + src/libtommath/mp_mod.c | 15 + src/libtommath/mp_mod_2d.c | 40 + .../mp_montgomery_calc_normalization.c | 43 + src/libtommath/mp_montgomery_reduce.c | 89 ++ src/libtommath/mp_montgomery_setup.c | 40 + src/libtommath/mp_mul.c | 68 + src/libtommath/mp_mul_2.c | 53 + src/libtommath/mp_mul_2d.c | 63 + src/libtommath/mp_mul_d.c | 68 + src/libtommath/mp_mulmod.c | 15 + src/libtommath/mp_neg.c | 18 + src/libtommath/mp_or.c | 54 + src/libtommath/mp_pack.c | 69 + src/libtommath/mp_pack_count.c | 12 + src/libtommath/mp_prime_fermat.c | 41 + src/libtommath/mp_prime_frobenius_underwood.c | 127 ++ src/libtommath/mp_prime_is_prime.c | 282 ++++ src/libtommath/mp_prime_miller_rabin.c | 91 ++ src/libtommath/mp_prime_next_prime.c | 127 ++ src/libtommath/mp_prime_rabin_miller_trials.c | 48 + src/libtommath/mp_prime_rand.c | 123 ++ .../mp_prime_strong_lucas_selfridge.c | 281 ++++ src/libtommath/mp_radix_size.c | 34 + src/libtommath/mp_radix_size_overestimate.c | 17 + src/libtommath/mp_rand.c | 39 + src/libtommath/mp_rand_source.c | 12 + src/libtommath/mp_read_radix.c | 69 + src/libtommath/mp_reduce.c | 83 ++ src/libtommath/mp_reduce_2k.c | 49 + src/libtommath/mp_reduce_2k_l.c | 52 + src/libtommath/mp_reduce_2k_setup.c | 30 + src/libtommath/mp_reduce_2k_setup_l.c | 28 + src/libtommath/mp_reduce_is_2k.c | 34 + src/libtommath/mp_reduce_is_2k_l.c | 27 + src/libtommath/mp_reduce_setup.c | 17 + src/libtommath/mp_root_n.c | 141 ++ src/libtommath/mp_rshd.c | 43 + src/libtommath/mp_sbin_size.c | 11 + src/libtommath/mp_set.c | 15 + src/libtommath/mp_set_double.c | 47 + src/libtommath/mp_set_i32.c | 7 + src/libtommath/mp_set_i64.c | 7 + src/libtommath/mp_set_l.c | 7 + src/libtommath/mp_set_u32.c | 7 + src/libtommath/mp_set_u64.c | 7 + src/libtommath/mp_set_ul.c | 7 + src/libtommath/mp_shrink.c | 22 + src/libtommath/mp_signed_rsh.c | 21 + src/libtommath/mp_sqrmod.c | 15 + src/libtommath/mp_sqrt.c | 67 + src/libtommath/mp_sqrtmod_prime.c | 133 ++ src/libtommath/mp_sub.c | 36 + src/libtommath/mp_sub_d.c | 78 + src/libtommath/mp_submod.c | 15 + src/libtommath/mp_to_radix.c | 95 ++ src/libtommath/mp_to_sbin.c | 22 + src/libtommath/mp_to_ubin.c | 37 + src/libtommath/mp_ubin_size.c | 12 + src/libtommath/mp_unpack.c | 49 + src/libtommath/mp_xor.c | 54 + src/libtommath/mp_zero.c | 13 + src/libtommath/s_mp_add.c | 70 + src/libtommath/s_mp_copy_digs.c | 23 + src/libtommath/s_mp_div_3.c | 64 + src/libtommath/s_mp_div_recursive.c | 159 +++ src/libtommath/s_mp_div_school.c | 156 ++ src/libtommath/s_mp_div_small.c | 51 + src/libtommath/s_mp_exptmod.c | 198 +++ src/libtommath/s_mp_exptmod_fast.c | 254 ++++ src/libtommath/s_mp_get_bit.c | 21 + src/libtommath/s_mp_invmod.c | 117 ++ src/libtommath/s_mp_invmod_odd.c | 116 ++ src/libtommath/s_mp_log.c | 81 ++ src/libtommath/s_mp_log_2expt.c | 12 + src/libtommath/s_mp_log_d.c | 65 + src/libtommath/s_mp_montgomery_reduce_comba.c | 119 ++ src/libtommath/s_mp_mul.c | 61 + src/libtommath/s_mp_mul_balance.c | 71 + src/libtommath/s_mp_mul_comba.c | 78 + src/libtommath/s_mp_mul_high.c | 52 + src/libtommath/s_mp_mul_high_comba.c | 70 + src/libtommath/s_mp_mul_karatsuba.c | 151 ++ src/libtommath/s_mp_mul_toom.c | 202 +++ src/libtommath/s_mp_prime_is_divisible.c | 33 + src/libtommath/s_mp_prime_tab.c | 44 + src/libtommath/s_mp_radix_map.c | 19 + src/libtommath/s_mp_radix_size_overestimate.c | 82 ++ src/libtommath/s_mp_rand_platform.c | 149 ++ src/libtommath/s_mp_sqr.c | 65 + src/libtommath/s_mp_sqr_comba.c | 87 ++ src/libtommath/s_mp_sqr_karatsuba.c | 92 ++ src/libtommath/s_mp_sqr_toom.c | 133 ++ src/libtommath/s_mp_sub.c | 56 + src/libtommath/s_mp_zero_buf.c | 22 + src/libtommath/s_mp_zero_digs.c | 23 + src/libtommath/tommath.h | 587 ++++++++ src/libtommath/tommath_c89.h | 40 + src/libtommath/tommath_class.h | 1260 +++++++++++++++++ src/libtommath/tommath_cutoffs.h | 13 + src/libtommath/tommath_private.h | 279 ++++ src/libtommath/tommath_superclass.h | 113 ++ src/llvm_backend.cpp | 72 +- src/main.cpp | 1 - 171 files changed, 10745 insertions(+), 1155 deletions(-) create mode 100644 src/libtommath/LICENSE create mode 100644 src/libtommath/README.md create mode 100644 src/libtommath/mp_2expt.c create mode 100644 src/libtommath/mp_abs.c create mode 100644 src/libtommath/mp_add.c create mode 100644 src/libtommath/mp_add_d.c create mode 100644 src/libtommath/mp_addmod.c create mode 100644 src/libtommath/mp_and.c create mode 100644 src/libtommath/mp_clamp.c create mode 100644 src/libtommath/mp_clear.c create mode 100644 src/libtommath/mp_clear_multi.c create mode 100644 src/libtommath/mp_cmp.c create mode 100644 src/libtommath/mp_cmp_d.c create mode 100644 src/libtommath/mp_cmp_mag.c create mode 100644 src/libtommath/mp_cnt_lsb.c create mode 100644 src/libtommath/mp_complement.c create mode 100644 src/libtommath/mp_copy.c create mode 100644 src/libtommath/mp_count_bits.c create mode 100644 src/libtommath/mp_cutoffs.c create mode 100644 src/libtommath/mp_div.c create mode 100644 src/libtommath/mp_div_2.c create mode 100644 src/libtommath/mp_div_2d.c create mode 100644 src/libtommath/mp_div_d.c create mode 100644 src/libtommath/mp_dr_is_modulus.c create mode 100644 src/libtommath/mp_dr_reduce.c create mode 100644 src/libtommath/mp_dr_setup.c create mode 100644 src/libtommath/mp_error_to_string.c create mode 100644 src/libtommath/mp_exch.c create mode 100644 src/libtommath/mp_expt_n.c create mode 100644 src/libtommath/mp_exptmod.c create mode 100644 src/libtommath/mp_exteuclid.c create mode 100644 src/libtommath/mp_fread.c create mode 100644 src/libtommath/mp_from_sbin.c create mode 100644 src/libtommath/mp_from_ubin.c create mode 100644 src/libtommath/mp_fwrite.c create mode 100644 src/libtommath/mp_gcd.c create mode 100644 src/libtommath/mp_get_double.c create mode 100644 src/libtommath/mp_get_i32.c create mode 100644 src/libtommath/mp_get_i64.c create mode 100644 src/libtommath/mp_get_l.c create mode 100644 src/libtommath/mp_get_mag_u32.c create mode 100644 src/libtommath/mp_get_mag_u64.c create mode 100644 src/libtommath/mp_get_mag_ul.c create mode 100644 src/libtommath/mp_grow.c create mode 100644 src/libtommath/mp_init.c create mode 100644 src/libtommath/mp_init_copy.c create mode 100644 src/libtommath/mp_init_i32.c create mode 100644 src/libtommath/mp_init_i64.c create mode 100644 src/libtommath/mp_init_l.c create mode 100644 src/libtommath/mp_init_multi.c create mode 100644 src/libtommath/mp_init_set.c create mode 100644 src/libtommath/mp_init_size.c create mode 100644 src/libtommath/mp_init_u32.c create mode 100644 src/libtommath/mp_init_u64.c create mode 100644 src/libtommath/mp_init_ul.c create mode 100644 src/libtommath/mp_invmod.c create mode 100644 src/libtommath/mp_is_square.c create mode 100644 src/libtommath/mp_kronecker.c create mode 100644 src/libtommath/mp_lcm.c create mode 100644 src/libtommath/mp_log_n.c create mode 100644 src/libtommath/mp_lshd.c create mode 100644 src/libtommath/mp_mod.c create mode 100644 src/libtommath/mp_mod_2d.c create mode 100644 src/libtommath/mp_montgomery_calc_normalization.c create mode 100644 src/libtommath/mp_montgomery_reduce.c create mode 100644 src/libtommath/mp_montgomery_setup.c create mode 100644 src/libtommath/mp_mul.c create mode 100644 src/libtommath/mp_mul_2.c create mode 100644 src/libtommath/mp_mul_2d.c create mode 100644 src/libtommath/mp_mul_d.c create mode 100644 src/libtommath/mp_mulmod.c create mode 100644 src/libtommath/mp_neg.c create mode 100644 src/libtommath/mp_or.c create mode 100644 src/libtommath/mp_pack.c create mode 100644 src/libtommath/mp_pack_count.c create mode 100644 src/libtommath/mp_prime_fermat.c create mode 100644 src/libtommath/mp_prime_frobenius_underwood.c create mode 100644 src/libtommath/mp_prime_is_prime.c create mode 100644 src/libtommath/mp_prime_miller_rabin.c create mode 100644 src/libtommath/mp_prime_next_prime.c create mode 100644 src/libtommath/mp_prime_rabin_miller_trials.c create mode 100644 src/libtommath/mp_prime_rand.c create mode 100644 src/libtommath/mp_prime_strong_lucas_selfridge.c create mode 100644 src/libtommath/mp_radix_size.c create mode 100644 src/libtommath/mp_radix_size_overestimate.c create mode 100644 src/libtommath/mp_rand.c create mode 100644 src/libtommath/mp_rand_source.c create mode 100644 src/libtommath/mp_read_radix.c create mode 100644 src/libtommath/mp_reduce.c create mode 100644 src/libtommath/mp_reduce_2k.c create mode 100644 src/libtommath/mp_reduce_2k_l.c create mode 100644 src/libtommath/mp_reduce_2k_setup.c create mode 100644 src/libtommath/mp_reduce_2k_setup_l.c create mode 100644 src/libtommath/mp_reduce_is_2k.c create mode 100644 src/libtommath/mp_reduce_is_2k_l.c create mode 100644 src/libtommath/mp_reduce_setup.c create mode 100644 src/libtommath/mp_root_n.c create mode 100644 src/libtommath/mp_rshd.c create mode 100644 src/libtommath/mp_sbin_size.c create mode 100644 src/libtommath/mp_set.c create mode 100644 src/libtommath/mp_set_double.c create mode 100644 src/libtommath/mp_set_i32.c create mode 100644 src/libtommath/mp_set_i64.c create mode 100644 src/libtommath/mp_set_l.c create mode 100644 src/libtommath/mp_set_u32.c create mode 100644 src/libtommath/mp_set_u64.c create mode 100644 src/libtommath/mp_set_ul.c create mode 100644 src/libtommath/mp_shrink.c create mode 100644 src/libtommath/mp_signed_rsh.c create mode 100644 src/libtommath/mp_sqrmod.c create mode 100644 src/libtommath/mp_sqrt.c create mode 100644 src/libtommath/mp_sqrtmod_prime.c create mode 100644 src/libtommath/mp_sub.c create mode 100644 src/libtommath/mp_sub_d.c create mode 100644 src/libtommath/mp_submod.c create mode 100644 src/libtommath/mp_to_radix.c create mode 100644 src/libtommath/mp_to_sbin.c create mode 100644 src/libtommath/mp_to_ubin.c create mode 100644 src/libtommath/mp_ubin_size.c create mode 100644 src/libtommath/mp_unpack.c create mode 100644 src/libtommath/mp_xor.c create mode 100644 src/libtommath/mp_zero.c create mode 100644 src/libtommath/s_mp_add.c create mode 100644 src/libtommath/s_mp_copy_digs.c create mode 100644 src/libtommath/s_mp_div_3.c create mode 100644 src/libtommath/s_mp_div_recursive.c create mode 100644 src/libtommath/s_mp_div_school.c create mode 100644 src/libtommath/s_mp_div_small.c create mode 100644 src/libtommath/s_mp_exptmod.c create mode 100644 src/libtommath/s_mp_exptmod_fast.c create mode 100644 src/libtommath/s_mp_get_bit.c create mode 100644 src/libtommath/s_mp_invmod.c create mode 100644 src/libtommath/s_mp_invmod_odd.c create mode 100644 src/libtommath/s_mp_log.c create mode 100644 src/libtommath/s_mp_log_2expt.c create mode 100644 src/libtommath/s_mp_log_d.c create mode 100644 src/libtommath/s_mp_montgomery_reduce_comba.c create mode 100644 src/libtommath/s_mp_mul.c create mode 100644 src/libtommath/s_mp_mul_balance.c create mode 100644 src/libtommath/s_mp_mul_comba.c create mode 100644 src/libtommath/s_mp_mul_high.c create mode 100644 src/libtommath/s_mp_mul_high_comba.c create mode 100644 src/libtommath/s_mp_mul_karatsuba.c create mode 100644 src/libtommath/s_mp_mul_toom.c create mode 100644 src/libtommath/s_mp_prime_is_divisible.c create mode 100644 src/libtommath/s_mp_prime_tab.c create mode 100644 src/libtommath/s_mp_radix_map.c create mode 100644 src/libtommath/s_mp_radix_size_overestimate.c create mode 100644 src/libtommath/s_mp_rand_platform.c create mode 100644 src/libtommath/s_mp_sqr.c create mode 100644 src/libtommath/s_mp_sqr_comba.c create mode 100644 src/libtommath/s_mp_sqr_karatsuba.c create mode 100644 src/libtommath/s_mp_sqr_toom.c create mode 100644 src/libtommath/s_mp_sub.c create mode 100644 src/libtommath/s_mp_zero_buf.c create mode 100644 src/libtommath/s_mp_zero_digs.c create mode 100644 src/libtommath/tommath.h create mode 100644 src/libtommath/tommath_c89.h create mode 100644 src/libtommath/tommath_class.h create mode 100644 src/libtommath/tommath_cutoffs.h create mode 100644 src/libtommath/tommath_private.h create mode 100644 src/libtommath/tommath_superclass.h diff --git a/Makefile b/Makefile index 86460cced..00d7d27d8 100644 --- a/Makefile +++ b/Makefile @@ -41,13 +41,16 @@ demo: ./odin run examples/demo/demo.odin debug: - $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin + $(CC) src/main.cpp src/libtommath.c $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin release: - $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin + $(CC) src/main.cpp src/libtommath.c $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin + +release-native: + $(CC) src/main.cpp src/libtommath.c $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin nightly: - $(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin + $(CC) src/main.cpp src/libtommath.c $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin diff --git a/build.bat b/build.bat index 038f02866..0e108653a 100644 --- a/build.bat +++ b/build.bat @@ -46,7 +46,7 @@ if %release_mode% EQU 0 ( rem Debug set compiler_warnings= ^ -W4 -WX ^ - -wd4100 -wd4101 -wd4127 -wd4189 ^ + -wd4100 -wd4101 -wd4127 -wd4146 -wd4189 ^ -wd4201 -wd4204 ^ -wd4456 -wd4457 -wd4480 ^ -wd4512 @@ -70,10 +70,19 @@ set linker_settings=%libs% %linker_flags% del *.pdb > NUL 2> NUL del *.ilk > NUL 2> NUL -cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name% +cl %compiler_settings% "src\main.cpp" "src\libtommath.c" /link %linker_settings% -OUT:%exe_name% +rem cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name% if %errorlevel% neq 0 goto end_of_build -if %release_mode% EQU 0 odin run examples/demo/demo.odin + +odin run examples/demo +rem odin run examples/bug -show-more-timings -threaded-checker +rem odin check examples/demo -threaded-checker +rem odin build examples/demo +rem odin run examples/demo -threaded-checker +rem odin run examples/bug -show-more-timings +rem if %release_mode% EQU 0 odin check examples/bug -show-more-timings +rem if %release_mode% EQU 0 odin check examples/bug del *.obj > NUL 2> NUL diff --git a/src/big_int.cpp b/src/big_int.cpp index 168e53fb2..f42c2e466 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -1,59 +1,14 @@ -struct BigInt { - union { - u64 word; - u64 *words; - } d; - i32 len; - b32 neg; -}; +#pragma warning(push) +#pragma warning(disable: 4146) +#include "libtommath/tommath.h" +#pragma warning(pop) - -BigInt const BIG_INT_ZERO = {{0}, 0, false}; -BigInt const BIG_INT_ONE = {{1}, 1, false}; -BigInt const BIG_INT_NEG_ONE = {{1}, 1, true}; - - -gb_global Arena global_big_int_arena = {0}; - -#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) -// URL(bill): https://stackoverflow.com/questions/8453146/128-bit-division-intrinsic-in-visual-c/8456388#8456388 -u8 udiv128_data[] = { - 0x48, 0x89, 0xD0, // mov rax,rdx - 0x48, 0x89, 0xCA, // mov rdx,rcx - 0x49, 0xF7, 0xF0, // div r8 - 0x49, 0x89, 0x11, // mov [r9],rdx - 0xC3 // ret -}; -u64 (__fastcall *unsafe_udiv128)(u64 numhi, u64 numlo, u64 den, u64* rem) = (u64 (__fastcall *)(u64, u64, u64, u64*))&udiv128_data[0]; +#ifndef MAX_BIG_INT_SHIFT +#define MAX_BIG_INT_SHIFT 1024 #endif -void global_big_int_init(void) { - arena_init(&global_big_int_arena, heap_allocator()); +typedef mp_int BigInt; -#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) - DWORD dummy; - VirtualProtect(udiv128_data, sizeof(udiv128_data), PAGE_EXECUTE_READWRITE, &dummy); -#endif -} - -// IMPORTANT NOTE LEAK(bill): This entire BigInt library leaks memory like there is no tomorrow -// However, this isn't really a problem as the vast majority of BigInt operations will not use -// more than 1 word. -// I could track how much this does leaks because I use an arena_allocator but I doubt I will require -// it any time soon -gb_inline gbAllocator big_int_allocator(void) { - return arena_allocator(&global_big_int_arena); -} - -void big_int_alloc(BigInt *dst, isize word_len, isize word_cap) { - GB_ASSERT_MSG(word_len <= word_cap, "%td %td", word_len, word_cap); - if (word_cap < dst->len) { - dst->len = cast(i32)word_len; - } else { - dst->len = cast(i32)word_len; - dst->d.words = gb_alloc_array(big_int_allocator(), u64, word_cap); - } -} void big_int_from_u64(BigInt *dst, u64 x); void big_int_from_i64(BigInt *dst, i64 x); @@ -61,10 +16,7 @@ void big_int_init (BigInt *dst, BigInt const *src); void big_int_from_string(BigInt *dst, String const &s); void big_int_dealloc(BigInt *dst) { - if (dst->len > 1) { - gb_free(big_int_allocator(), dst->d.words); - } - zero_item(dst); + mp_clear(dst); } BigInt big_int_make(BigInt const *b, bool abs=false); @@ -77,19 +29,6 @@ i64 big_int_to_i64 (BigInt const *x); f64 big_int_to_f64 (BigInt const *x); String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base = 10); -gb_inline u64 const *big_int_ptr(BigInt const *b) { - if (b->len <= 1) { - return &b->d.word; - } - return b->d.words; -} -gb_inline u64 *big_int_ptr(BigInt *b) { - if (b->len <= 1) { - return &b->d.word; - } - return b->d.words; -} - void big_int_add (BigInt *dst, BigInt const *x, BigInt const *y); void big_int_sub (BigInt *dst, BigInt const *x, BigInt const *y); void big_int_shl (BigInt *dst, BigInt const *x, BigInt const *y); @@ -101,9 +40,6 @@ void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q, BigInt *r); void big_int_quo (BigInt *z, BigInt const *x, BigInt const *y); void big_int_rem (BigInt *z, BigInt const *x, BigInt const *y); -void big_int_euclidean_div(BigInt *z, BigInt const *x, BigInt const *y); -void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y); - void big_int_and (BigInt *dst, BigInt const *x, BigInt const *y); void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y); void big_int_xor (BigInt *dst, BigInt const *x, BigInt const *y); @@ -159,99 +95,37 @@ void big_int_rem_eq(BigInt *dst, BigInt const *x) { -void big_int_normalize(BigInt *dst) { - if (dst->len == 1 && dst->d.word == 0) { - dst->len = 0; - return; - } - u64 const *words = big_int_ptr(dst); - - i32 count_minus_one = -1; - for (i32 i = 0; i < dst->len; i++) { - u64 word = words[i]; - if (word != 0) { - count_minus_one = i; - } - } - - if (count_minus_one < 0) { - dst->neg = false; - if (words[0] == 0) { - dst->len = 0; - return; - } - } - dst->len = count_minus_one+1; - if (count_minus_one == 0) { - dst->d.word = words[0]; - } -} - i64 big_int_sign(BigInt const *x) { - if (x->len == 0) { + if (mp_iszero(x)) { return 0; } - return x->neg ? -1 : +1; + return x->sign == MP_ZPOS ? +1 : -1; } void big_int_from_u64(BigInt *dst, u64 x) { - if (x == 0) { - dst->len = 0; - dst->neg = false; - } else { - dst->len = 1; - dst->d.word = x; - dst->neg = false; - } + mp_init_u64(dst, x); } void big_int_from_i64(BigInt *dst, i64 x) { - if (x >= 0) { - big_int_from_u64(dst, cast(u64)x); - return; - } else { - dst->len = 1; - dst->d.word = (cast(u64)(-(x+1ll))) + 1ull; - dst->neg = true; - } + mp_init_i64(dst, x); } void big_int_init(BigInt *dst, BigInt const *src) { if (dst == src) { return; } - if (src->len == 0) { - big_int_from_u64(dst, 0); - return; - } else if (src->len == 1) { - dst->len = 1; - dst->d.word = src->d.word; - dst->neg = src->neg; - return; - } - - - dst->neg = src->neg; - big_int_alloc(dst, src->len, src->len); - u64 const *s = big_int_ptr(src); - gb_memmove(dst->d.words, s, gb_size_of(u64)*dst->len); - big_int_normalize(dst); + mp_init_copy(dst, src); } BigInt big_int_make(BigInt const *b, bool abs) { BigInt i = {}; big_int_init(&i, b); - if (abs) i.neg = false; + if (abs) mp_abs(&i, &i); return i; } BigInt big_int_make_abs(BigInt const *b) { return big_int_make(b, true); } -BigInt big_int_alias_abs(BigInt const *b) { - BigInt x = *b; - x.neg = false; - return x; -} BigInt big_int_make_u64(u64 x) { @@ -289,7 +163,7 @@ void big_int_from_string(BigInt *dst, String const &s) { BigInt b = {}; big_int_from_u64(&b, base); - big_int_init(dst, &BIG_INT_ZERO); + mp_zero(dst); isize i = 0; for (; i < len; i++) { @@ -331,476 +205,79 @@ void big_int_from_string(BigInt *dst, String const &s) { big_int_mul_eq(dst, &b); } } - big_int_normalize(dst); } u64 big_int_to_u64(BigInt const *x) { - GB_ASSERT(!x->neg); - switch (x->len) { - case 0: return 0; - case 1: return x->d.word; - } - GB_PANIC("BigInt too big for u64"); - return 0; + GB_ASSERT(x->sign == 0); + return mp_get_u64(x); } i64 big_int_to_i64(BigInt const *x) { - switch (x->len) { - case 0: - return 0; - case 1: - if (x->neg) { - if (x->d.word <= 9223372036854775808ull) { // 2^63 - 1 - return (-cast(i64)(x->d.word-1ull)) - 1ll; - } else { - GB_PANIC("BigInt too big for i64"); - } - } else { - return cast(i64)x->d.word; - } - break; - } - - GB_PANIC("BigInt too big for i64"); - return 0; + return mp_get_i64(x); } f64 big_int_to_f64(BigInt const *x) { - switch (x->len) { - case 0: - return 0.0; - case 1: - if (x->neg) { - i64 i = big_int_to_i64(x); - return cast(f64)i; - } else { - u64 u = big_int_to_u64(x); - return cast(f64)u; - } - } - - - u64 const *words = big_int_ptr(x); - f64 base = pow(2.0, gb_size_of(u64)); - // TODO(bill): clean up this code and make it more accurate - f64 res = 0; - for (isize i = x->len-1; i >= 0; i--) { - res *= base; - u64 w = words[i]; - res += cast(f64)w; - } - return res; -} - -bool bi__alias(BigInt const *dst, BigInt const *src) { - if (dst == src) { - return true; - } - if (dst->len > 1 && src->len > 1) { - return dst->d.words == src->d.words; - } - return false; + return mp_get_double(x); } void big_int_neg(BigInt *dst, BigInt const *x) { - big_int_init(dst, x); - dst->neg = !dst->neg; - big_int_normalize(dst); + mp_neg(x, dst); } int big_int_cmp(BigInt const *x, BigInt const *y) { - if (x == y) { - return 0; - } else if (x->neg && !y->neg) { - return -1; - } else if (!x->neg && y->neg) { - return +1; - } else if (x->len > y->len) { - return x->neg ? -1 : +1; - } else if (y->len > x->len) { - return x->neg ? +1 : -1; - } else if (x->len == 0) { - return 0; - } - - u64 const *xd = big_int_ptr(x); - u64 const *yd = big_int_ptr(y); - - for (i32 i = x->len-1; i >= 0; i--) { - u64 a = xd[i]; - u64 b = yd[i]; - - if (a > b) { - return x->neg ? -1 : +1; - } - if (a < b) { - return x->neg ? +1 : -1; - } - } - - return 0; + return mp_cmp(x, y); } int big_int_cmp_zero(BigInt const *x) { - if (x->len == 0) { + if (mp_iszero(x)) { return 0; } - return x->neg ? -1 : +1; + return x->sign ? -1 : +1; } bool big_int_is_zero(BigInt const *x) { - if (x->len == 0) { - return true; - } - return false; + return mp_iszero(x); } void big_int_add(BigInt *dst, BigInt const *x, BigInt const *y) { - if (x->len == 0) { - big_int_init(dst, y); - return; - } - if (y->len == 0) { - big_int_init(dst, x); - return; - } - - if (x->neg == y->neg) { - dst->neg = x->neg; - - u64 const *x_words = big_int_ptr(x); - u64 const *y_words = big_int_ptr(y); - u64 overflow = cast(u64)add_overflow_u64(x_words[0], y_words[0], &dst->d.word); - if (overflow == 0 && x->len == 1 && y->len == 1) { - dst->len = 1; - big_int_normalize(dst); - return; - } - - u64 first_word = dst->d.word; - big_int_alloc(dst, 0, gb_max(x->len, y->len)+1); - dst->d.words[0] = first_word; - - i32 i = 1; - for (;;) { - u64 v = overflow; - overflow = 0; - - bool found_word = false; - if (i < x->len) { - found_word = true; - overflow += add_overflow_u64(v, x_words[i], &v); - } - if (i < y->len) { - found_word = true; - overflow += add_overflow_u64(v, y_words[i], &v); - } - - dst->d.words[i] = v; - i += 1; - - if (!found_word) { - dst->len = i; - big_int_normalize(dst); - return; - } - } - } else { - BigInt const *pos = nullptr; - BigInt const *neg = nullptr; - if (x->neg) { - neg = x; - pos = y; - } else { - GB_ASSERT(y->neg); - pos = x; - neg = y; - } - - BigInt neg_abs = {}; - big_int_neg(&neg_abs, neg); - BigInt const *bigger = nullptr; - BigInt const *smaller = nullptr; - - int cmp = big_int_cmp(pos, &neg_abs); - dst->neg = cmp < 0; - switch (cmp) { - case 0: - big_int_from_u64(dst, 0); - return; - case -1: - bigger = &neg_abs; - smaller = pos; - break; - case +1: - bigger = pos; - smaller = &neg_abs; - break; - default: - GB_PANIC("Invalid big_int_cmp value"); - return; - } - - u64 const *bigger_words = big_int_ptr(bigger); - u64 const *smaller_words = big_int_ptr(smaller); - u64 overflow = cast(u64)sub_overflow_u64(bigger_words[0], smaller_words[0], &dst->d.word); - if (overflow == 0 && bigger->len == 1 && smaller->len == 1) { - dst->len = 1; - big_int_normalize(dst); - return; - } - - u64 first_word = dst->d.word; - big_int_alloc(dst, 0, bigger->len); - dst->d.words[0] = first_word; - - i32 i = 0; - while (i < bigger->len) { - u64 v = bigger_words[i]; - u64 prev_overflow = overflow; - overflow = 0; - - bool found_word = false; - if (i < smaller->len) { - found_word = true; - overflow += sub_overflow_u64(v, smaller_words[i], &v); - } - if (sub_overflow_u64(v, prev_overflow, &v)) { - found_word = true; - overflow += 1; - } else { - // IMPORTANT TODO(bill): Is this mathematics correct here? - v += overflow; - } - dst->d.words[i] = v; - i += 1; - - if (!found_word) { - break; - } - } - - if (overflow != 0) { - GB_ASSERT_MSG(overflow == 0, "%p %p %p", dst, x, y); - } - dst->len = i; - big_int_normalize(dst); - return; - } + mp_add(x, y, dst); } void big_int_sub(BigInt *dst, BigInt const *x, BigInt const *y) { - BigInt neg_y = {}; - big_int_neg(&neg_y, y); - big_int_add(dst, x, &neg_y); - big_int_normalize(dst); - return; + mp_sub(x, y, dst); } void big_int_shl(BigInt *dst, BigInt const *x, BigInt const *y) { - GB_ASSERT(!y->neg); - - if (x->len == 0) { - big_int_from_u64(dst, 0); - return; - } - - if (x->len == 1 && x->d.word == 0) { - big_int_from_u64(dst, 0); - return; - } - - if (y->len == 0) { - big_int_init(dst, x); - return; - } - - if (y->len == 0) { - big_int_from_u64(dst, 0); - return; - } - - if (y->len != 1) { - GB_PANIC("SHL value greater than 64 bits!"); - } - - u64 const *xd = big_int_ptr(x); - u64 shift_amount = big_int_to_u64(y); - if (x->len == 1 && shift_amount < 64) { - dst->d.word = xd[0] << shift_amount; - if (dst->d.word > xd[0]) { - dst->len = 1; - dst->neg = x->neg; - big_int_normalize(dst); - return; - } - } - - u64 word_shift_len = shift_amount / 64; - u64 remaining_shift_len = shift_amount % 64; - - big_int_alloc(dst, cast(i32)word_shift_len, x->len + word_shift_len + 1); - GB_ASSERT((x->len + word_shift_len + 1) > 1); - - u64 carry = 0; - for (i32 i = 0; i < x->len; i++) { - u64 word = xd[i]; - dst->d.words[dst->len] = carry | (word << remaining_shift_len); - dst->len += 1; - if (remaining_shift_len > 0) { - carry = word >> (64 - remaining_shift_len); - } else { - carry = 0; - } - } - big_int_normalize(dst); + u32 yy = mp_get_u32(y); + mp_mul_2d(x, yy, dst); } void big_int_shr(BigInt *dst, BigInt const *x, BigInt const *y) { - GB_ASSERT(!y->neg); - - if (x->len == 0) { - big_int_from_u64(dst, 0); - return; - } - if (x->len == 1 && x->d.word == 0) { - big_int_from_u64(dst, 0); - return; - } - - if (y->len == 0) { - big_int_init(dst, x); - return; - } - - if (y->len != 1) { - GB_PANIC("SHR value greater than 64 bits!"); - } - - u64 const *xd = big_int_ptr(x); - u64 shift_amount = big_int_to_u64(y); - - if (x->len == 1) { - dst->d.word = xd[0] >> shift_amount; - dst->len = 1; - dst->neg = x->neg; - big_int_normalize(dst); - return; - } - - u64 word_shift_len = shift_amount / 64ull; - u64 remaining_shift_len = shift_amount % 64ull; - - if (word_shift_len >= x->len) { - big_int_from_u64(dst, 0); - return; - } - - i32 len = cast(i32)(x->len - word_shift_len); - i32 cap = gb_max(len, dst->len); - big_int_alloc(dst, len, cap); - GB_ASSERT(dst->len >= 1); - - u64 carry = 0; - for (i32 src_idx = x->len - 1; src_idx >= 0; src_idx--) { - u64 v = xd[src_idx]; - u64 dst_idx = src_idx - word_shift_len; - - dst->d.words[dst_idx] = carry | (v >> remaining_shift_len); - - carry = v << (64ull - remaining_shift_len); - } - big_int_normalize(dst); + u32 yy = mp_get_u32(y); + BigInt d = {}; + mp_div_2d(x, yy, dst, &d); + mp_clear(&d); } void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) { - BigInt v64 = {}; - big_int_from_u64(&v64, 64); - - big_int_from_u64(dst, 0); - - u64 const *xd = big_int_ptr(x); - for (i32 i = x->len-1; i >= 0; i--) { - BigInt shifted = {}; - big_int_shl(&shifted, dst, &v64); - - u64 result_u64 = 0; - u64 carry_u64 = 0; - mul_overflow_u64(y, xd[i], &result_u64, &carry_u64); - - BigInt result; - BigInt carry; - BigInt carry_shifted; - big_int_from_u64(&result, result_u64); - big_int_from_u64(&carry, carry_u64); - big_int_shl(&carry_shifted, &carry, &v64); - - BigInt tmp; - big_int_add(&tmp, &shifted, &carry_shifted); - big_int_add(dst, &tmp, &result); - } - big_int_normalize(dst); + BigInt d = {}; + big_int_from_u64(&d, y); + mp_mul(x, &d, dst); + mp_clear(&d); } -void big_int_mul(BigInt *z, BigInt const *x, BigInt const *y) { - if (x->len == 0 || y->len == 0) { - return big_int_from_u64(z, 0); - } - u64 const *xd = big_int_ptr(x); - u64 const *yd = big_int_ptr(y); - - u64 carry = 0; - mul_overflow_u64(xd[0], yd[0], &z->d.word, &carry); - if (carry == 0 && x->len == 1 && y->len == 1) { - z->neg = (x->neg != y->neg); - z->len = 1; - big_int_normalize(z); - return; - } - - big_int_from_u64(z, 0); - i32 len = x->len+y->len; - big_int_alloc(z, len, len); - u64 *zd = big_int_ptr(z); - - for (i32 i = 0; i < y->len; i++) { - u64 d = yd[i]; - if (d != 0) { - u64 *z = zd+i; - i32 n = x->len; - u64 c = 0; - for (i32 j = 0; j < n; j++) { - u64 z1 = 0; - u64 z00 = 0; - mul_overflow_u64(xd[j], d, &z00, &z1); - u64 z0 = z00 + z[j]; - if (z0 < z00) { - z1 += 1; - } - z[j] = z0 + c; - c = 0; - if (z[j] < z0) { - c = 1; - } - c += z1; - } - - zd[n+i] = c; - } - } - - z->neg = (x->neg != y->neg); - big_int_normalize(z); +void big_int_mul(BigInt *dst, BigInt const *x, BigInt const *y) { + mp_mul(x, y, dst); } @@ -813,216 +290,6 @@ u64 leading_zeros_u64(u64 x) { } -void bi__divWW(u64 u1, u64 u0, u64 y, u64 *q, u64 *r) { -#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) - *q = unsafe_udiv128(u1, u0, y, r); -#else - // NOTE(bill): q = (u1<<64 + u0 - r)/y - // Hacker's Delight page 152 - if (u1 >= y) { - *q = ~cast(u64)0ull; - *r = ~cast(u64)0ull; - return; - } - - - u64 s = leading_zeros_u64(y); - y <<= s; - - static u64 const B = 1ull<<32ull; - static u64 const M = B-1ull; - - u64 vn1 = y >> 32ull; - u64 vn0 = y & M; - u64 un32 = (u1<>(64ull-s)); - u64 un10 = u0 << s; - u64 un1 = un10 >> 32ull; - u64 un0 = un10 & M; - u64 q1 = un32 / vn1; - u64 rhat = un32 - (q1*vn1); - - while ((q1 >= B) || ((q1*vn0) > (B*rhat+un1))) { - q1 -= 1; - rhat += vn1; - if (rhat >= B) { - break; - } - } - - u64 un21 = (un32*B) + un1 - (q1*y); - u64 q0 = un21 / vn1; - rhat = un21 - (q0*vn1); - - while ((q0 >= B) || ((q0*vn0) > (B*rhat+un0))) { - q0 -= 1; - rhat += vn1; - if (rhat >= B) { - break; - } - } - - *q = q1*B + q0; - *r = (un21*B + un0 - q0*y) >> s; -#endif -} - - -void bi__divWVW(BigInt *z, u64 xn, BigInt const *x, u64 y, u64 *r_) { - GB_ASSERT(x->len >= z->len); - u64 r = xn; - u64 const *xd = big_int_ptr(x); - u64 *zd = big_int_ptr(z); - for (i32 i = z->len-1; i >= 0; i--) { - u64 u1 = r; - u64 u0 = xd[i]; - bi__divWW(r, xd[i], y, &zd[i], &r); - } - if (r_) *r_ = r; -} - -void bi__divW(BigInt const *x, u64 y, BigInt *q_, u64 *r_) { - BigInt q = {}; - u64 r = 0; - i32 m = x->len; - if (y == 0) { - GB_PANIC("division by zero"); - } else if (y == 1) { - q = *x; - } else if (m == 0) { - // okay - } else { - big_int_alloc(&q, m, m); - bi__divWVW(&q, 0, x, y, &r); - big_int_normalize(&q); - } - if (q_) *q_ = q; - if (r_) *r_ = r; -} - -u64 shlVU(BigInt *z, BigInt const *x, u64 s) { - u64 c = 0; - i32 n = z->len; - - u64 const *xd = big_int_ptr(x); - u64 *zd = big_int_ptr(z); - - if (n > 0) { - u64 s1 = 64 - s; - u64 w1 = xd[n-1]; - c = w1 >> s1; - for (i32 i = n-1; i > 0; i--) { - u64 w = w1; - w1 = xd[i-1]; - zd[i] = (w<>s1); - } - zd[0] = w1 << s; - } - return c; -} - -u64 mulAddVWW(BigInt *z, BigInt const *x, u64 y, u64 r) { - u64 c = r; - u64 const *xd = big_int_ptr(x); - u64 *zd = big_int_ptr(z); - - for (i32 i = 0; i < z->len; i++) { - u64 a, b; - mul_overflow_u64(xd[i], y, &a, &b); - zd[i] = b + c; - if (zd[i] < b) { - a += 1; - } - c = a; - } - return c; -} - -bool bi__greater_than(u64 x1, u64 x2, u64 y1, u64 y2) { - return x1 > y1 || (x1 == x2 && x2 > y2); -} - -void bi__div_large(BigInt const *a, BigInt const *b, BigInt *q, BigInt *r) { - i32 n = b->len; - i32 m = a->len - n; - - BigInt u = *a; - BigInt v = *b; - - big_int_alloc(q, m+1, m+1); - - BigInt qhatv = {{cast(u64)n+1ull}, 1, false}; - - big_int_alloc(&u, a->len+1, a->len+1); - - u64 *ud = big_int_ptr(&u); - u64 *vd = big_int_ptr(&v); - - u64 shift = leading_zeros_u64(vd[n-1]); - if (shift > 0) { - BigInt v1 = {{cast(u64)n}, 1, false}; - shlVU(&v1, &v, shift); - v = v1; - vd = big_int_ptr(&v); - } - - BigInt uu = u; - uu.len = a->len; - ud[a->len] = shlVU(&uu, a, shift); -} - -void big_int_quo_rem_unsigned(BigInt const *a, BigInt const *b, BigInt *q_, BigInt *r_) { - if (b->len == 0) { - GB_PANIC("division by zero"); - } else if (b->len == 1 && b->d.word == 0) { - GB_PANIC("division by zero"); - } - - BigInt x = *a; x.neg = false; - BigInt y = *b; y.neg = false; - BigInt q = {}; - BigInt r = {}; - - int cmp = big_int_cmp(&x, &y); - - if (cmp < 0) { - q = BIG_INT_ZERO; - r = x; - goto end; - } else if (cmp == 0) { - q = BIG_INT_ONE; - r = BIG_INT_ZERO; - goto end; - } - - if (y.len == 1) { - if (y.d.word == 0) { - GB_PANIC("division by zero"); - } else if (y.d.word == 1) { - q = x; - r = BIG_INT_ZERO; - goto end; - } else if (x.len == 0) { - q = BIG_INT_ZERO; - r = BIG_INT_ZERO; - goto end; - } - u64 rr = 0; - bi__divW(&x, y.d.word, &q, &rr); - big_int_from_u64(&r, rr); - goto end; - } - - GB_PANIC("Division of a large denominator not yet supported"); - bi__div_large(&x, &y, &q, &r); - -end: - big_int_normalize(&q); - big_int_normalize(&r); - if (q_) *q_ = q; - if (r_) *r_ = r; - return; -} - // `big_int_quo_rem` sets z to the quotient x/y and r to the remainder x%y // and returns the pair (z, r) for y != 0. // if y == 0, a division-by-zero run-time panic occurs. @@ -1030,52 +297,29 @@ end: // q = x/y with the result truncated to zero // r = x - y*q void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q_, BigInt *r_) { - BigInt q = {}; - BigInt r = {}; - - big_int_quo_rem_unsigned(x, y, &q, &r); - q.neg = q.len > 0 && x->neg != y->neg; - r.neg = r.len > 0 && x->neg; - - if (q_) *q_ = q; - if (r_) *r_ = r; + mp_div(x, y, q_, r_); } void big_int_quo(BigInt *z, BigInt const *x, BigInt const *y) { BigInt r = {}; - big_int_quo_rem_unsigned(x, y, z, &r); - z->neg = z->len > 0 && x->neg != y->neg; + big_int_quo_rem(x, y, z, &r); + mp_clear(&r); } void big_int_rem(BigInt *z, BigInt const *x, BigInt const *y) { BigInt q = {}; - big_int_quo_rem_unsigned(x, y, &q, z); - z->neg = z->len > 0 && x->neg; + big_int_quo_rem(x, y, &q, z); + mp_clear(&q); } - - -void big_int_euclidean_div(BigInt *z, BigInt const *x, BigInt const *y) { - BigInt r = {}; - big_int_quo_rem(x, y, z, &r); - if (r.neg) { - if (y->neg) { - big_int_add(z, z, &BIG_INT_ONE); - } else { - big_int_sub(z, z, &BIG_INT_ONE); - } - } -} - - void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) { BigInt y0 = {}; big_int_init(&y0, y); BigInt q = {}; big_int_quo_rem(x, y, &q, z); - if (z->neg) { - if (y0.neg) { + if (z->sign) { + if (y0.sign) { big_int_sub(z, z, &y0); } else { big_int_add(z, z, &y0); @@ -1084,360 +328,127 @@ void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) { } + void big_int_and(BigInt *dst, BigInt const *x, BigInt const *y) { - if (x->len == 0 || y->len == 0) { - big_int_from_u64(dst, 0); - return; - } - - if (x->neg == y->neg) { - if (x->neg) { - // (-x) & (-y) == ~(x-1) & ~(x-y) == ~((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - BigInt x1 = big_int_make_abs(x); - BigInt y1 = big_int_make_abs(y); - BigInt z1 = {}; - - big_int_sub_eq(&x1, &BIG_INT_ONE); - big_int_sub_eq(&y1, &BIG_INT_ONE); - big_int_or(&z1, &x1, &y1); - big_int_add(dst, &z1, &BIG_INT_ONE); - dst->neg = true; // NOTE(bill): dst cannot be 0 as x and y are both negative - big_int_normalize(dst); - return; - } - u64 const *xd = big_int_ptr(x); - u64 const *yd = big_int_ptr(y); - - if (x->len == 1 && y->len == 1) { - dst->len = 1; - dst->d.word = xd[0] & yd[0]; - return; - } - - i32 len = gb_max(x->len, y->len); - big_int_alloc(dst, len, len); - GB_ASSERT(dst->len > 1); - - i32 i = 0; - for (; i < x->len && i < y->len; i++) { - dst->d.words[i] = xd[i] & yd[i]; - } - for (; i < len; i++) { - dst->d.words[i] = 0; - } - dst->neg = false; - big_int_normalize(dst); - return; - } - if (x->neg) { - BigInt const *tmp = x; - x = y; - y = tmp; - // NOTE(bill): AND is symmetric - } - - - // x & (-y) == x &~ (y-1) - - dst->neg = false; - BigInt x1 = big_int_make_abs(x); - BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&y1, &BIG_INT_ONE); - big_int_and_not(dst, &x1, &y1); - big_int_normalize(dst); -} - -void big_int__and_not_abs(BigInt *dst, BigInt const *x, BigInt const *y) { - u64 const *xd = big_int_ptr(x); - u64 const *yd = big_int_ptr(y); - - if (x->len == 1 && y->len == 1) { - dst->len = 1; - dst->d.word = xd[0] & (~yd[0]); - return; - } - - i32 len = gb_max(x->len, y->len); - big_int_alloc(dst, len, len); - GB_ASSERT(dst->len > 1); - - i32 i = 0; - for (; i < x->len && i < y->len; i++) { - dst->d.words[i] = xd[i] & (~yd[i]); - } - if (i < x->len) { - for (; i < len; i++) { - dst->d.words[i] = xd[i]; - } - } - if (i < y->len) { - for (; i < len; i++) { - dst->d.words[i] = yd[i]; - } - } - big_int_normalize(dst); + mp_and(x, y, dst); } void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) { - if (x->len == 0) { + if (mp_iszero(x)) { big_int_init(dst, y); return; } - if (y->len == 0) { + if (mp_iszero(y)) { big_int_init(dst, x); return; } - if (x->neg == y->neg) { - if (x->neg) { + if (x->sign == y->sign) { + if (x->sign) { // (-x) &~ (-y) == ~(x-1) &~ ~(y-1) == ~(x-1) & (y-1) == (y-1) &~ (x-1) BigInt x1 = big_int_make_abs(x); BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&x1, &BIG_INT_ONE); - big_int_sub_eq(&y1, &BIG_INT_ONE); + mp_decr(&x1); + mp_decr(&y1); - big_int__and_not_abs(dst, &y1, &x1); - dst->neg = false; - big_int_normalize(dst); + BigInt ny1 = {}; + mp_complement(&y1, &ny1); + mp_and(&x1, &ny1, dst); + + mp_clear(&x1); + mp_clear(&y1); + mp_clear(&ny1); return; } - big_int__and_not_abs(dst, x, y); - dst->neg = false; - big_int_normalize(dst); + BigInt ny = {}; + mp_complement(y, &ny); + mp_and(x, &ny, dst); + + mp_clear(&ny); return; } - if (x->neg) { + if (x->sign) { // (-x) &~ y == ~(x-1) &~ y == ~(x-1) & ~y == ~((x-1) | y) == -(((x-1) | y) + 1) BigInt x1 = big_int_make_abs(x); BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&x1, &BIG_INT_ONE); + mp_decr(&x1); BigInt z1 = {}; big_int_or(&z1, &x1, &y1); - big_int_add(dst, &z1, &BIG_INT_ONE); - dst->neg = true; - big_int_normalize(dst); + mp_add_d(&z1, 1, dst); + + mp_clear(&x1); + mp_clear(&y1); + mp_clear(&z1); return; } // x &~ (-y) == x &~ ~(y-1) == x & (y-1) BigInt x1 = big_int_make_abs(x); BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&y1, &BIG_INT_ONE); + mp_decr(&y1); big_int_and(dst, &x1, &y1); - dst->neg = false; - big_int_normalize(dst); + + mp_clear(&x1); + mp_clear(&y1); return; } - -void big_int__xor_abs(BigInt *dst, BigInt const *x, BigInt const *y) { - u64 const *xd = big_int_ptr(x); - u64 const *yd = big_int_ptr(y); - - if (x->len == 1 && y->len == 1) { - dst->len = 1; - dst->d.word = xd[0] ^ yd[0]; - big_int_normalize(dst); - return; - } - - i32 len = gb_max(x->len, y->len); - big_int_alloc(dst, len, len); - GB_ASSERT(dst->len > 1); - - i32 i = 0; - for (; i < x->len && i < y->len; i++) { - dst->d.words[i] = xd[i] ^ yd[i]; - } - for (; i < len; i++) { - if (i < x->len) { - dst->d.words[i] = xd[i]; - } - if (i < y->len) { - dst->d.words[i] = yd[i]; - } - } - big_int_normalize(dst); -} - void big_int_xor(BigInt *dst, BigInt const *x, BigInt const *y) { - if (x->len == 0) { - big_int_init(dst, y); - return; - } else if (y->len == 0) { - big_int_init(dst, x); - return; - } - - if (x->neg == y->neg) { - if (x->neg) { - // (-x) ^ (-y) == ~(x-1) ^ ~(y-1) == (x-1) ^ (y-1) - BigInt x1 = big_int_make_abs(x); - BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&x1, &BIG_INT_ONE); - big_int_sub_eq(&y1, &BIG_INT_ONE); - big_int__xor_abs(dst, &x1, &y1); - dst->neg = false; - return; - } - - big_int__xor_abs(dst, x, y); - dst->neg = false; - return; - } - - // x->neg != y->neg - if (x->neg) { - BigInt const *tmp = x; - x = y; - y = tmp; - } - dst->neg = false; - if (y->neg) { - // x ^ (-y) == x ^ ~(y-1) == ~(x ^ (y-1)) == -((x ^ (y-1)) + 1) - BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&y1, &BIG_INT_ONE); - big_int__xor_abs(dst, x, &y1); - big_int_add_eq(dst, &BIG_INT_ONE); - dst->neg = true; - } - return; + mp_xor(x, y, dst); } void big_int_or(BigInt *dst, BigInt const *x, BigInt const *y) { - if (x->len == 0) { - big_int_init(dst, y); - return; - } else if (y->len == 0) { - big_int_init(dst, x); - return; - } + mp_or(x, y, dst); +} - if (x->neg == y->neg) { - if (x->neg) { - // (-x) | (-y) == ~(x-1) | ~(y-1) == ~((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - BigInt x1 = big_int_make_abs(x); - BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&x1, &BIG_INT_ONE); - big_int_sub_eq(&y1, &BIG_INT_ONE); - big_int_and(dst, &x1, &y1); - big_int_add_eq(dst, &BIG_INT_ONE); - dst->neg = true; - return; - } - - dst->neg = x->neg; - u64 const *xd = big_int_ptr(x); - u64 const *yd = big_int_ptr(y); - - if (x->len == 1 && y->len == 1) { - dst->len = 1; - dst->d.word = xd[0] | yd[0]; - return; - } - - i32 len = gb_max(x->len, y->len); - big_int_alloc(dst, len, len); - GB_ASSERT(dst->len > 1); - - for (i32 i = 0; i < len; i++) { - u64 word = 0; - if (i < x->len) { - word |= xd[i]; - } - if (i < y->len) { - word |= yd[i]; - } - - dst->d.words[i] = word; - } - big_int_normalize(dst); - } - - if (x->neg) { - BigInt const *tmp = x; - x = y; - y = tmp; - } - dst->neg = false; - if (y->neg) { - // x | (-y) == x | ~(y-1) == ~((y-1) &~ x) == -(~((y-1) &~ x) + 1) - BigInt y1 = big_int_make_abs(y); - big_int_sub_eq(&y1, &BIG_INT_ONE); - big_int__and_not_abs(dst, &y1, x); - big_int_add_eq(dst, &BIG_INT_ONE); - dst->neg = true; - } - return; +void debug_print_big_int(BigInt const *x) { + String s = big_int_to_string(temporary_allocator(), x, 10); + gb_printf_err("[DEBUG] %.*s\n", LIT(s)); } -void big_int_not(BigInt *dst, BigInt const *x, u64 bit_count, bool is_signed) { +void big_int_not(BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed) { + GB_ASSERT(bit_count >= 0); if (bit_count == 0) { big_int_from_u64(dst, 0); return; } - // TODO(bill): Is this fast enough? + BigInt pow2b = {}; + mp_2expt(&pow2b, bit_count); - dst->neg = false; - u64 const *xd = big_int_ptr(x); - if (bit_count <= 64) { - dst->len = 1; - if (x->len == 0) { - if (bit_count == 64) { - dst->d.word = ~cast(u64)0ull; - } else { - dst->d.word = (1ull << bit_count) - 1ull; - } - } else if (x->len == 1) { - dst->d.word = ~xd[0]; - if (bit_count != 64) { - u64 mask = (1ull << bit_count) - 1ull; - dst->d.word &= mask; - } - } - } else { - dst->len = cast(i32)((bit_count+63ull) / 64ull); - GB_ASSERT(dst->len >= x->len); - big_int_alloc(dst, dst->len, dst->len); - GB_ASSERT(dst->len > 1); + BigInt mask = {}; + mp_2expt(&mask, bit_count); + mp_decr(&mask); - i32 i = 0; - for (; i < x->len; i++) { - dst->d.words[i] = ~xd[i]; - } - for (; i < dst->len; i++) { - dst->d.words[i] = ~cast(u64)0ull; - } + BigInt v = {}; + mp_init_copy(&v, x); + mp_mod_2d(&v, bit_count, &v); - i32 word_idx = cast(i32)(cast(u64)dst->len - (bit_count/64ull)-1ull); - u32 word_bit_idx = bit_count % 64; - if (word_idx < dst->len) { - u64 mask = (1ull << word_bit_idx) - 1ull; - dst->d.words[word_idx] &= mask; - } - } - - big_int_normalize(dst); + mp_xor(&v, &mask, dst); if (is_signed) { - BigInt prec = big_int_make_u64(bit_count-1); - BigInt mask = {}; - BigInt mask_minus_one = {}; - big_int_shl(&mask, &BIG_INT_ONE, &prec); - big_int_sub(&mask_minus_one, &mask, &BIG_INT_ONE); + BigInt pmask = {}; + BigInt pmask_minus_one = {}; + mp_2expt(&pmask, bit_count-1); + mp_sub_d(&pmask, 1, &pmask_minus_one); BigInt a = {}; BigInt b = {}; - big_int_and(&a, dst, &mask_minus_one); - big_int_and(&b, dst, &mask); + big_int_and(&a, dst, &pmask_minus_one); + big_int_and(&b, dst, &pmask); big_int_sub(dst, &a, &b); + mp_clear(&a); + mp_clear(&b); } + + mp_clear(&pow2b); + mp_clear(&mask); + mp_clear(&v); } @@ -1454,7 +465,7 @@ char digit_to_char(u8 digit) { String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base) { GB_ASSERT(base <= 16); - if ((x->len == 0) && (x->len == 1 && x->d.word == 0)) { + if (mp_iszero(x)) { u8 *buf = gb_alloc_array(allocator, u8, 1); buf[0] = '0'; return make_string(buf, 1); @@ -1463,12 +474,12 @@ String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base) { Array buf = {}; array_init(&buf, allocator, 0, 32); - BigInt v = *x; + BigInt v = {}; + mp_init_copy(&v, x); - if (v.neg) { + if (v.sign) { array_add(&buf, '-'); - v.neg = false; - big_int_normalize(&v); + mp_abs(&v, &v); } isize first_word_idx = buf.count; @@ -1488,6 +499,8 @@ String big_int_to_string(gbAllocator allocator, BigInt const *x, u64 base) { digit = cast(u8)big_int_to_u64(&r); array_add(&buf, digit_to_char(digit)); + mp_clear(&r); + mp_clear(&b); for (isize i = first_word_idx; i < buf.count/2; i++) { isize j = buf.count + first_word_idx - i - 1; diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 2d25aaae4..3beaba2e8 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -745,7 +745,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 return false; } - if (op.value.value_integer.neg) { + if (op.value.value_integer.sign) { error(op.expr, "Negative 'swizzle' index"); return false; } @@ -795,10 +795,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) { - if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.value = exact_value_to_float(x.value); + y.value = exact_value_to_float(y.value); + if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) { x.type = t_untyped_float; } - if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) { y.type = t_untyped_float; } } @@ -882,16 +884,20 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { - if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.value = exact_value_to_float(x.value); + y.value = exact_value_to_float(y.value); + z.value = exact_value_to_float(z.value); + w.value = exact_value_to_float(w.value); + if (is_type_numeric(x.type) && x.value.kind == ExactValue_Float) { x.type = t_untyped_float; } - if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + if (is_type_numeric(y.type) && y.value.kind == ExactValue_Float) { y.type = t_untyped_float; } - if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) { + if (is_type_numeric(z.type) && z.value.kind == ExactValue_Float) { z.type = t_untyped_float; } - if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) { + if (is_type_numeric(w.type) && w.value.kind == ExactValue_Float) { w.type = t_untyped_float; } } @@ -1484,7 +1490,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (operand->mode == Addressing_Constant) { switch (operand->value.kind) { case ExactValue_Integer: - operand->value.value_integer.neg = false; + mp_abs(&operand->value.value_integer, &operand->value.value_integer); break; case ExactValue_Float: operand->value.value_float = gb_abs(operand->value.value_float); @@ -1837,7 +1843,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_invalid; return false; } - if (x.value.value_integer.neg) { + if (x.value.value_integer.sign) { error(call, "Negative vector element length"); operand->mode = Addressing_Type; operand->type = t_invalid; @@ -1877,7 +1883,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->type = t_invalid; return false; } - if (x.value.value_integer.neg) { + if (x.value.value_integer.sign) { error(call, "Negative array element length"); operand->mode = Addressing_Type; operand->type = t_invalid; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 54d610c0f..2e552ee79 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1503,13 +1503,13 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ big_int_from_i64(&bi127, 127); big_int_shl_eq(&umax, &bi128); - big_int_sub_eq(&umax, &BIG_INT_ONE); + mp_decr(&umax); big_int_shl_eq(&imin, &bi127); big_int_neg(&imin, &imin); big_int_shl_eq(&imax, &bi127); - big_int_sub_eq(&imax, &BIG_INT_ONE); + mp_decr(&imax); } switch (type->Basic.kind) { @@ -1555,7 +1555,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ { // return 0ull <= i && i <= umax; int b = big_int_cmp(&i, &umax); - return !i.neg && (b <= 0); + return !i.sign && (b <= 0); } case Basic_UntypedInteger: @@ -1758,12 +1758,6 @@ void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - #if 0 - gb_printf_err("AddressingMode, %d\n", o->mode); - gb_printf_err("ExactValueKind, %d\n", o->value.kind); - bool ok = check_representable_as_constant(ctx, o->value, type, &out_value); - gb_printf_err("ok, %d\n", ok); - #endif error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s", a, b, c); check_assignment_error_suggestion(ctx, o, type); } @@ -2206,7 +2200,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ } BigInt max_shift = {}; - big_int_from_u64(&max_shift, 128); + big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT); if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) { gbString err_str = expr_to_string(y->expr); @@ -2248,7 +2242,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ } } - if (y->mode == Addressing_Constant && y->value.value_integer.neg) { + if (y->mode == Addressing_Constant && y->value.value_integer.sign) { gbString err_str = expr_to_string(y->expr); error(node, "Shift amount cannot be negative: '%s'", err_str); gb_string_free(err_str); @@ -3320,7 +3314,7 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 if (operand.mode == Addressing_Constant && (c->state_flags & StateFlag_no_bounds_check) == 0) { BigInt i = exact_value_to_integer(operand.value).value_integer; - if (i.neg && !is_type_enum(index_type)) { + if (i.sign && !is_type_enum(index_type)) { gbString expr_str = expr_to_string(operand.expr); error(operand.expr, "Index '%s' cannot be a negative value", expr_str); gb_string_free(expr_str); @@ -3366,7 +3360,7 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 } else { // NOTE(bill): Do array bound checking i64 v = -1; - if (i.len <= 1) { + if (i.used <= 1) { v = big_int_to_i64(&i); } if (value) *value = v; diff --git a/src/check_type.cpp b/src/check_type.cpp index cc4ffebca..e8000d66c 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -207,7 +207,7 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) { if (is_type_untyped(type) || is_type_integer(type)) { if (o.value.kind == ExactValue_Integer) { BigInt v = o.value.value_integer; - if (v.len > 1) { + if (v.used > 1) { gbAllocator a = heap_allocator(); String str = big_int_to_string(a, &v); error(node, "#align too large, %.*s", LIT(str)); @@ -1998,16 +1998,16 @@ i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) { if (is_type_untyped(type) || is_type_integer(type)) { if (o->value.kind == ExactValue_Integer) { BigInt count = o->value.value_integer; - if (o->value.value_integer.neg) { + if (o->value.value_integer.sign) { gbAllocator a = heap_allocator(); String str = big_int_to_string(a, &count); error(e, "Invalid negative array count, %.*s", LIT(str)); gb_free(a, str.text); return 0; } - switch (count.len) { + switch (count.used) { case 0: return 0; - case 1: return count.d.word; + case 1: return big_int_to_u64(&count); } gbAllocator a = heap_allocator(); String str = big_int_to_string(a, &count); diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 9bcaf76de..cd5ba6579 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -75,8 +75,8 @@ HashKey hash_exact_value(ExactValue v) { } case ExactValue_Integer: { - HashKey key = hashing_proc(big_int_ptr(&v.value_integer), v.value_integer.len * gb_size_of(u64)); - u8 last = (u8)v.value_integer.neg; + HashKey key = hashing_proc(v.value_integer.dp, gb_size_of(*v.value_integer.dp) * v.value_integer.used); + u8 last = (u8)v.value_integer.sign; key.key = (key.key ^ last) * 0x100000001b3ll; return key; } @@ -719,7 +719,6 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) case Token_Shr: big_int_shr(&c, a, b); break; default: goto error; } - big_int_normalize(&c); ExactValue res = {ExactValue_Integer}; res.value_integer = c; return res; diff --git a/src/libtommath/LICENSE b/src/libtommath/LICENSE new file mode 100644 index 000000000..b23b3c867 --- /dev/null +++ b/src/libtommath/LICENSE @@ -0,0 +1,26 @@ + The LibTom license + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/src/libtommath/README.md b/src/libtommath/README.md new file mode 100644 index 000000000..be5b20783 --- /dev/null +++ b/src/libtommath/README.md @@ -0,0 +1,44 @@ +# libtommath + +This is the git repository for [LibTomMath](http://www.libtom.net/LibTomMath/), a free open source portable number theoretic multiple-precision integer (MPI) library written entirely in C. + +## Build Status + +### Travis CI + +master: [![Build Status](https://api.travis-ci.org/libtom/libtommath.png?branch=master)](https://travis-ci.org/libtom/libtommath) + +develop: [![Build Status](https://api.travis-ci.org/libtom/libtommath.png?branch=develop)](https://travis-ci.org/libtom/libtommath) + +### AppVeyor + +master: [![Build status](https://ci.appveyor.com/api/projects/status/b80lpolw3i8m6hsh/branch/master?svg=true)](https://ci.appveyor.com/project/libtom/libtommath/branch/master) + +develop: [![Build status](https://ci.appveyor.com/api/projects/status/b80lpolw3i8m6hsh/branch/develop?svg=true)](https://ci.appveyor.com/project/libtom/libtommath/branch/develop) + +### ABI Laboratory + +API/ABI changes: [check here](https://abi-laboratory.pro/tracker/timeline/libtommath/) + +## Summary + +The `develop` branch contains the in-development version. Stable releases are tagged. + +Documentation is built from the LaTeX file `bn.tex`. There is also limited documentation in `tommath.h`. +There is also a document, `tommath.pdf`, which describes the goals of the project and many of the algorithms used. + +The project can be build by using `make`. Along with the usual `make`, `make clean` and `make install`, +there are several other build targets, see the makefile for details. +There are also makefiles for certain specific platforms. + +## Testing + +Tests are located in `demo/` and can be built in two flavors. +* `make test` creates a stand-alone test binary that executes several test routines. +* `make mtest_opponent` creates a test binary that is intended to be run against `mtest`. + `mtest` can be built with `make mtest` and test execution is done like `./mtest/mtest | ./mtest_opponent`. + `mtest` is creating test vectors using an alternative MPI library and `test` is consuming these vectors to verify correct behavior of ltm + +## Building and Installing + +Building is straightforward for GNU Linux only, the section "Building LibTomMath" in the documentation in `doc/bn.pdf` has the details. diff --git a/src/libtommath/mp_2expt.c b/src/libtommath/mp_2expt.c new file mode 100644 index 000000000..66e857478 --- /dev/null +++ b/src/libtommath/mp_2expt.c @@ -0,0 +1,31 @@ +#include "tommath_private.h" +#ifdef MP_2EXPT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* computes a = 2**b + * + * Simple algorithm which zeroes the int, grows it then just sets one bit + * as required. + */ +mp_err mp_2expt(mp_int *a, int b) +{ + mp_err err; + + /* zero a as per default */ + mp_zero(a); + + /* grow a to accomodate the single bit */ + if ((err = mp_grow(a, (b / MP_DIGIT_BIT) + 1)) != MP_OKAY) { + return err; + } + + /* set the used count of where the bit will go */ + a->used = (b / MP_DIGIT_BIT) + 1; + + /* put the single bit in its place */ + a->dp[b / MP_DIGIT_BIT] = (mp_digit)1 << (mp_digit)(b % MP_DIGIT_BIT); + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_abs.c b/src/libtommath/mp_abs.c new file mode 100644 index 000000000..a87cc0cb9 --- /dev/null +++ b/src/libtommath/mp_abs.c @@ -0,0 +1,24 @@ +#include "tommath_private.h" +#ifdef MP_ABS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* b = |a| + * + * Simple function copies the input and fixes the sign to positive + */ +mp_err mp_abs(const mp_int *a, mp_int *b) +{ + mp_err err; + + /* copy a to b */ + if ((err = mp_copy(a, b)) != MP_OKAY) { + return err; + } + + /* force the sign of b to positive */ + b->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_add.c b/src/libtommath/mp_add.c new file mode 100644 index 000000000..bf7a61e25 --- /dev/null +++ b/src/libtommath/mp_add.c @@ -0,0 +1,29 @@ +#include "tommath_private.h" +#ifdef MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* high level addition (handles signs) */ +mp_err mp_add(const mp_int *a, const mp_int *b, mp_int *c) +{ + /* handle two cases, not four */ + if (a->sign == b->sign) { + /* both positive or both negative */ + /* add their magnitudes, copy the sign */ + c->sign = a->sign; + return s_mp_add(a, b, c); + } + + /* 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) { + MP_EXCH(const mp_int *, a, b); + } + + c->sign = a->sign; + return s_mp_sub(a, b, c); +} + +#endif diff --git a/src/libtommath/mp_add_d.c b/src/libtommath/mp_add_d.c new file mode 100644 index 000000000..c57a80db3 --- /dev/null +++ b/src/libtommath/mp_add_d.c @@ -0,0 +1,86 @@ +#include "tommath_private.h" +#ifdef MP_ADD_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* single digit addition */ +mp_err mp_add_d(const mp_int *a, mp_digit b, mp_int *c) +{ + mp_err err; + int oldused; + + /* fast path for a == c */ + if (a == c) { + if (!mp_isneg(c) && + !mp_iszero(c) && + ((c->dp[0] + b) < MP_DIGIT_MAX)) { + c->dp[0] += b; + return MP_OKAY; + } + if (mp_isneg(c) && + (c->dp[0] > b)) { + c->dp[0] -= b; + return MP_OKAY; + } + } + + /* grow c as required */ + if ((err = mp_grow(c, a->used + 1)) != MP_OKAY) { + return err; + } + + /* if a is negative and |a| >= b, call c = |a| - b */ + if (mp_isneg(a) && ((a->used > 1) || (a->dp[0] >= b))) { + mp_int a_ = *a; + /* temporarily fix sign of a */ + a_.sign = MP_ZPOS; + + /* c = |a| - b */ + err = mp_sub_d(&a_, b, c); + + /* fix sign */ + c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return err; + } + + /* old number of used digits in c */ + oldused = c->used; + + /* if a is positive */ + if (!mp_isneg(a)) { + /* add digits, mu is carry */ + int i; + mp_digit mu = b; + for (i = 0; i < a->used; i++) { + c->dp[i] = a->dp[i] + mu; + mu = c->dp[i] >> MP_DIGIT_BIT; + c->dp[i] &= MP_MASK; + } + /* set final carry */ + c->dp[i] = mu; + + /* setup size */ + c->used = a->used + 1; + } else { + /* a was negative and |a| < b */ + c->used = 1; + + /* the result is a single digit */ + c->dp[0] = (a->used == 1) ? b - a->dp[0] : b; + } + + /* sign always positive */ + c->sign = MP_ZPOS; + + /* now zero to oldused */ + s_mp_zero_digs(c->dp + c->used, oldused - c->used); + mp_clamp(c); + + return MP_OKAY; +} + +#endif diff --git a/src/libtommath/mp_addmod.c b/src/libtommath/mp_addmod.c new file mode 100644 index 000000000..91e2087e5 --- /dev/null +++ b/src/libtommath/mp_addmod.c @@ -0,0 +1,15 @@ +#include "tommath_private.h" +#ifdef MP_ADDMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* d = a + b (mod c) */ +mp_err mp_addmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d) +{ + mp_err err; + if ((err = mp_add(a, b, d)) != MP_OKAY) { + return err; + } + return mp_mod(d, c, d); +} +#endif diff --git a/src/libtommath/mp_and.c b/src/libtommath/mp_and.c new file mode 100644 index 000000000..b5230c4d1 --- /dev/null +++ b/src/libtommath/mp_and.c @@ -0,0 +1,54 @@ +#include "tommath_private.h" +#ifdef MP_AND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* two complement and */ +mp_err mp_and(const mp_int *a, const mp_int *b, mp_int *c) +{ + int used = MP_MAX(a->used, b->used) + 1, i; + mp_err err; + mp_digit ac = 1, bc = 1, cc = 1; + bool neg = (mp_isneg(a) && mp_isneg(b)); + + if ((err = mp_grow(c, used)) != MP_OKAY) { + return err; + } + + for (i = 0; i < used; i++) { + mp_digit x, y; + + /* convert to two complement if negative */ + if (mp_isneg(a)) { + ac += (i >= a->used) ? MP_MASK : (~a->dp[i] & MP_MASK); + x = ac & MP_MASK; + ac >>= MP_DIGIT_BIT; + } else { + x = (i >= a->used) ? 0uL : a->dp[i]; + } + + /* convert to two complement if negative */ + if (mp_isneg(b)) { + bc += (i >= b->used) ? MP_MASK : (~b->dp[i] & MP_MASK); + y = bc & MP_MASK; + bc >>= MP_DIGIT_BIT; + } else { + y = (i >= b->used) ? 0uL : b->dp[i]; + } + + c->dp[i] = x & y; + + /* convert to to sign-magnitude if negative */ + if (neg) { + cc += ~c->dp[i] & MP_MASK; + c->dp[i] = cc & MP_MASK; + cc >>= MP_DIGIT_BIT; + } + } + + c->used = used; + c->sign = (neg ? MP_NEG : MP_ZPOS); + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_clamp.c b/src/libtommath/mp_clamp.c new file mode 100644 index 000000000..ae59c4016 --- /dev/null +++ b/src/libtommath/mp_clamp.c @@ -0,0 +1,27 @@ +#include "tommath_private.h" +#ifdef MP_CLAMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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] == 0u)) { + --(a->used); + } + + /* reset the sign flag if zero */ + if (mp_iszero(a)) { + a->sign = MP_ZPOS; + } +} +#endif diff --git a/src/libtommath/mp_clear.c b/src/libtommath/mp_clear.c new file mode 100644 index 000000000..11094b262 --- /dev/null +++ b/src/libtommath/mp_clear.c @@ -0,0 +1,20 @@ +#include "tommath_private.h" +#ifdef MP_CLEAR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* clear one (frees) */ +void mp_clear(mp_int *a) +{ + /* only do anything if a hasn't been freed previously */ + if (a->dp != NULL) { + /* free ram */ + MP_FREE_DIGS(a->dp, a->alloc); + + /* reset members to make debugging easier */ + a->dp = NULL; + a->alloc = a->used = 0; + a->sign = MP_ZPOS; + } +} +#endif diff --git a/src/libtommath/mp_clear_multi.c b/src/libtommath/mp_clear_multi.c new file mode 100644 index 000000000..9c7aed831 --- /dev/null +++ b/src/libtommath/mp_clear_multi.c @@ -0,0 +1,18 @@ +#include "tommath_private.h" +#ifdef MP_CLEAR_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include + +void mp_clear_multi(mp_int *mp, ...) +{ + va_list args; + va_start(args, mp); + while (mp != NULL) { + mp_clear(mp); + mp = va_arg(args, mp_int *); + } + va_end(args); +} +#endif diff --git a/src/libtommath/mp_cmp.c b/src/libtommath/mp_cmp.c new file mode 100644 index 000000000..9f3847b84 --- /dev/null +++ b/src/libtommath/mp_cmp.c @@ -0,0 +1,21 @@ +#include "tommath_private.h" +#ifdef MP_CMP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* compare two ints (signed)*/ +mp_ord mp_cmp(const mp_int *a, const mp_int *b) +{ + /* compare based on sign */ + if (a->sign != b->sign) { + return mp_isneg(a) ? MP_LT : MP_GT; + } + + /* if negative compare opposite direction */ + if (mp_isneg(a)) { + MP_EXCH(const mp_int *, a, b); + } + + return mp_cmp_mag(a, b); +} +#endif diff --git a/src/libtommath/mp_cmp_d.c b/src/libtommath/mp_cmp_d.c new file mode 100644 index 000000000..42f7b1600 --- /dev/null +++ b/src/libtommath/mp_cmp_d.c @@ -0,0 +1,26 @@ +#include "tommath_private.h" +#ifdef MP_CMP_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* compare a digit */ +mp_ord mp_cmp_d(const mp_int *a, mp_digit b) +{ + /* compare based on sign */ + if (mp_isneg(a)) { + 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 a->dp[0] > b ? MP_GT : MP_LT; + } + + return MP_EQ; +} +#endif diff --git a/src/libtommath/mp_cmp_mag.c b/src/libtommath/mp_cmp_mag.c new file mode 100644 index 000000000..e5e502b8c --- /dev/null +++ b/src/libtommath/mp_cmp_mag.c @@ -0,0 +1,25 @@ +#include "tommath_private.h" +#ifdef MP_CMP_MAG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* compare maginitude of two ints (unsigned) */ +mp_ord mp_cmp_mag(const mp_int *a, const mp_int *b) +{ + int n; + + /* compare based on # of non-zero digits */ + if (a->used != b->used) { + return a->used > b->used ? MP_GT : MP_LT; + } + + /* compare based on digits */ + for (n = a->used; n --> 0;) { + if (a->dp[n] != b->dp[n]) { + return a->dp[n] > b->dp[n] ? MP_GT : MP_LT; + } + } + + return MP_EQ; +} +#endif diff --git a/src/libtommath/mp_cnt_lsb.c b/src/libtommath/mp_cnt_lsb.c new file mode 100644 index 000000000..8519ad1b0 --- /dev/null +++ b/src/libtommath/mp_cnt_lsb.c @@ -0,0 +1,38 @@ +#include "tommath_private.h" +#ifdef MP_CNT_LSB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +static const char 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(const mp_int *a) +{ + int x; + mp_digit q; + + /* easy out */ + if (mp_iszero(a)) { + return 0; + } + + /* scan lower digits until non-zero */ + for (x = 0; (x < a->used) && (a->dp[x] == 0u); x++) {} + q = a->dp[x]; + x *= MP_DIGIT_BIT; + + /* now scan this digit until a 1 is found */ + if ((q & 1u) == 0u) { + mp_digit p; + do { + p = q & 15u; + x += lnz[p]; + q >>= 4; + } while (p == 0u); + } + return x; +} + +#endif diff --git a/src/libtommath/mp_complement.c b/src/libtommath/mp_complement.c new file mode 100644 index 000000000..c16e25f9d --- /dev/null +++ b/src/libtommath/mp_complement.c @@ -0,0 +1,13 @@ +#include "tommath_private.h" +#ifdef MP_COMPLEMENT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* b = ~a */ +mp_err mp_complement(const mp_int *a, mp_int *b) +{ + mp_int a_ = *a; + a_.sign = ((a_.sign == MP_ZPOS) && !mp_iszero(a)) ? MP_NEG : MP_ZPOS; + return mp_sub_d(&a_, 1uL, b); +} +#endif diff --git a/src/libtommath/mp_copy.c b/src/libtommath/mp_copy.c new file mode 100644 index 000000000..d79e2b8bf --- /dev/null +++ b/src/libtommath/mp_copy.c @@ -0,0 +1,29 @@ +#include "tommath_private.h" +#ifdef MP_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* copy, b = a */ +mp_err mp_copy(const mp_int *a, mp_int *b) +{ + mp_err err; + + /* if dst == src do nothing */ + if (a == b) { + return MP_OKAY; + } + + /* grow dest */ + if ((err = mp_grow(b, a->used)) != MP_OKAY) { + return err; + } + + /* copy everything over and zero high digits */ + s_mp_copy_digs(b->dp, a->dp, a->used); + s_mp_zero_digs(b->dp + a->used, b->used - a->used); + b->used = a->used; + b->sign = a->sign; + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_count_bits.c b/src/libtommath/mp_count_bits.c new file mode 100644 index 000000000..52b463d46 --- /dev/null +++ b/src/libtommath/mp_count_bits.c @@ -0,0 +1,28 @@ +#include "tommath_private.h" +#ifdef MP_COUNT_BITS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* returns the number of bits in an int */ +int mp_count_bits(const mp_int *a) +{ + int r; + mp_digit q; + + /* shortcut */ + if (mp_iszero(a)) { + return 0; + } + + /* get number of digits and add that */ + r = (a->used - 1) * MP_DIGIT_BIT; + + /* take the last digit and count the bits in it */ + q = a->dp[a->used - 1]; + while (q > 0u) { + ++r; + q >>= 1u; + } + return r; +} +#endif diff --git a/src/libtommath/mp_cutoffs.c b/src/libtommath/mp_cutoffs.c new file mode 100644 index 000000000..45b0beec1 --- /dev/null +++ b/src/libtommath/mp_cutoffs.c @@ -0,0 +1,14 @@ +#include "tommath_private.h" +#ifdef MP_CUTOFFS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifndef MP_FIXED_CUTOFFS +#include "tommath_cutoffs.h" +int MP_MUL_KARATSUBA_CUTOFF = MP_DEFAULT_MUL_KARATSUBA_CUTOFF, + MP_SQR_KARATSUBA_CUTOFF = MP_DEFAULT_SQR_KARATSUBA_CUTOFF, + MP_MUL_TOOM_CUTOFF = MP_DEFAULT_MUL_TOOM_CUTOFF, + MP_SQR_TOOM_CUTOFF = MP_DEFAULT_SQR_TOOM_CUTOFF; +#endif + +#endif diff --git a/src/libtommath/mp_div.c b/src/libtommath/mp_div.c new file mode 100644 index 000000000..b092d7bb0 --- /dev/null +++ b/src/libtommath/mp_div.c @@ -0,0 +1,42 @@ +#include "tommath_private.h" +#ifdef MP_DIV_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +mp_err mp_div(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d) +{ + mp_err err; + + /* is divisor zero ? */ + if (mp_iszero(b)) { + return MP_VAL; + } + + /* if a < b then q = 0, r = a */ + if (mp_cmp_mag(a, b) == MP_LT) { + if (d != NULL) { + if ((err = mp_copy(a, d)) != MP_OKAY) { + return err; + } + } + if (c != NULL) { + mp_zero(c); + } + return MP_OKAY; + } + + if (MP_HAS(S_MP_DIV_RECURSIVE) + && (b->used > (2 * MP_MUL_KARATSUBA_CUTOFF)) + && (b->used <= ((a->used/3)*2))) { + err = s_mp_div_recursive(a, b, c, d); + } else if (MP_HAS(S_MP_DIV_SCHOOL)) { + err = s_mp_div_school(a, b, c, d); + } else if (MP_HAS(S_MP_DIV_SMALL)) { + err = s_mp_div_small(a, b, c, d); + } else { + err = MP_VAL; + } + + return err; +} +#endif diff --git a/src/libtommath/mp_div_2.c b/src/libtommath/mp_div_2.c new file mode 100644 index 000000000..8ab9bcb9c --- /dev/null +++ b/src/libtommath/mp_div_2.c @@ -0,0 +1,40 @@ +#include "tommath_private.h" +#ifdef MP_DIV_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* b = a/2 */ +mp_err mp_div_2(const mp_int *a, mp_int *b) +{ + mp_err err; + int x, oldused; + mp_digit r; + + if ((err = mp_grow(b, a->used)) != MP_OKAY) { + return err; + } + + oldused = b->used; + b->used = a->used; + + /* carry */ + r = 0; + for (x = b->used; x --> 0;) { + /* get the carry for the next iteration */ + mp_digit rr = a->dp[x] & 1u; + + /* shift the current digit, add in carry and store */ + b->dp[x] = (a->dp[x] >> 1) | (r << (MP_DIGIT_BIT - 1)); + + /* forward carry to next iteration */ + r = rr; + } + + /* zero excess digits */ + s_mp_zero_digs(b->dp + b->used, oldused - b->used); + + b->sign = a->sign; + mp_clamp(b); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_div_2d.c b/src/libtommath/mp_div_2d.c new file mode 100644 index 000000000..e523465af --- /dev/null +++ b/src/libtommath/mp_div_2d.c @@ -0,0 +1,61 @@ +#include "tommath_private.h" +#ifdef MP_DIV_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ +mp_err mp_div_2d(const mp_int *a, int b, mp_int *c, mp_int *d) +{ + mp_err err; + + if (b < 0) { + return MP_VAL; + } + + if ((err = mp_copy(a, c)) != MP_OKAY) { + return err; + } + + /* 'a' should not be used after here - it might be the same as d */ + + /* get the remainder */ + if (d != NULL) { + if ((err = mp_mod_2d(a, b, d)) != MP_OKAY) { + return err; + } + } + + /* shift by as many digits in the bit count */ + if (b >= MP_DIGIT_BIT) { + mp_rshd(c, b / MP_DIGIT_BIT); + } + + /* shift any bit count < MP_DIGIT_BIT */ + b %= MP_DIGIT_BIT; + if (b != 0u) { + int x; + mp_digit r, mask, shift; + + /* mask */ + mask = ((mp_digit)1 << b) - 1uL; + + /* shift for lsb */ + shift = (mp_digit)(MP_DIGIT_BIT - b); + + /* carry */ + r = 0; + for (x = c->used; x --> 0;) { + /* get the lower bits of this word in a temp */ + mp_digit rr = c->dp[x] & mask; + + /* shift the current word and mix in the carry bits from the previous word */ + c->dp[x] = (c->dp[x] >> b) | (r << shift); + + /* set the carry to the carry bits of the current word found above */ + r = rr; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_div_d.c b/src/libtommath/mp_div_d.c new file mode 100644 index 000000000..5697e545c --- /dev/null +++ b/src/libtommath/mp_div_d.c @@ -0,0 +1,84 @@ +#include "tommath_private.h" +#ifdef MP_DIV_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* single digit division (based on routine from MPI) */ +mp_err mp_div_d(const mp_int *a, mp_digit b, mp_int *c, mp_digit *d) +{ + mp_int q; + mp_word w; + mp_err err; + int ix; + + /* cannot divide by zero */ + if (b == 0u) { + return MP_VAL; + } + + /* quick outs */ + if ((b == 1u) || mp_iszero(a)) { + if (d != NULL) { + *d = 0; + } + if (c != NULL) { + return mp_copy(a, c); + } + return MP_OKAY; + } + + /* power of two ? */ + if (MP_HAS(MP_DIV_2) && (b == 2u)) { + if (d != NULL) { + *d = mp_isodd(a) ? 1u : 0u; + } + return (c == NULL) ? MP_OKAY : mp_div_2(a, c); + } + if (MP_HAS(MP_DIV_2D) && MP_IS_2EXPT(b)) { + ix = 1; + while ((ix < MP_DIGIT_BIT) && (b != (((mp_digit)1)<dp[0] & (((mp_digit)1<<(mp_digit)ix) - 1uL); + } + return (c == NULL) ? MP_OKAY : mp_div_2d(a, ix, c, NULL); + } + + /* three? */ + if (MP_HAS(S_MP_DIV_3) && (b == 3u)) { + return s_mp_div_3(a, c, d); + } + + /* no easy answer [c'est la vie]. Just division */ + if ((err = mp_init_size(&q, a->used)) != MP_OKAY) { + return err; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used; ix --> 0;) { + mp_digit t = 0; + w = (w << (mp_word)MP_DIGIT_BIT) | (mp_word)a->dp[ix]; + if (w >= b) { + t = (mp_digit)(w / b); + w -= (mp_word)t * (mp_word)b; + } + q.dp[ix] = t; + } + + if (d != NULL) { + *d = (mp_digit)w; + } + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + } + mp_clear(&q); + + return MP_OKAY; +} + +#endif diff --git a/src/libtommath/mp_dr_is_modulus.c b/src/libtommath/mp_dr_is_modulus.c new file mode 100644 index 000000000..72b3c9681 --- /dev/null +++ b/src/libtommath/mp_dr_is_modulus.c @@ -0,0 +1,27 @@ +#include "tommath_private.h" +#ifdef MP_DR_IS_MODULUS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* determines if a number is a valid DR modulus */ +bool mp_dr_is_modulus(const mp_int *a) +{ + int ix; + + /* must be at least two digits */ + if (a->used < 2) { + return false; + } + + /* 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 false; + } + } + return true; +} + +#endif diff --git a/src/libtommath/mp_dr_reduce.c b/src/libtommath/mp_dr_reduce.c new file mode 100644 index 000000000..f0f6f35e6 --- /dev/null +++ b/src/libtommath/mp_dr_reduce.c @@ -0,0 +1,68 @@ +#include "tommath_private.h" +#ifdef MP_DR_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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 Joong 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 + */ +mp_err mp_dr_reduce(mp_int *x, const mp_int *n, mp_digit k) +{ + mp_err err; + + /* m = digits in modulus */ + int m = n->used; + + /* ensure that "x" has at least 2m digits */ + 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. + */ + for (;;) { + int i; + mp_digit mu = 0; + + /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ + for (i = 0; i < m; i++) { + mp_word r = ((mp_word)x->dp[i + m] * (mp_word)k) + x->dp[i] + mu; + x->dp[i] = (mp_digit)(r & MP_MASK); + mu = (mp_digit)(r >> ((mp_word)MP_DIGIT_BIT)); + } + + /* set final carry */ + x->dp[i] = mu; + + /* zero words above m */ + s_mp_zero_digs(x->dp + m + 1, (x->used - m) - 1); + + /* 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) { + break; + } + + if ((err = s_mp_sub(x, n, x)) != MP_OKAY) { + return err; + } + } + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_dr_setup.c b/src/libtommath/mp_dr_setup.c new file mode 100644 index 000000000..c5bb359a3 --- /dev/null +++ b/src/libtommath/mp_dr_setup.c @@ -0,0 +1,15 @@ +#include "tommath_private.h" +#ifdef MP_DR_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* determines the setup value */ +void mp_dr_setup(const mp_int *a, mp_digit *d) +{ + /* the casts are required if MP_DIGIT_BIT is one less than + * the number of bits in a mp_digit [e.g. MP_DIGIT_BIT==31] + */ + *d = (mp_digit)(((mp_word)1 << (mp_word)MP_DIGIT_BIT) - (mp_word)a->dp[0]); +} + +#endif diff --git a/src/libtommath/mp_error_to_string.c b/src/libtommath/mp_error_to_string.c new file mode 100644 index 000000000..39adcd124 --- /dev/null +++ b/src/libtommath/mp_error_to_string.c @@ -0,0 +1,29 @@ +#include "tommath_private.h" +#ifdef MP_ERROR_TO_STRING_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* return a char * string for a given code */ +const char *mp_error_to_string(mp_err code) +{ + switch (code) { + case MP_OKAY: + return "Successful"; + case MP_ERR: + return "Unknown error"; + case MP_MEM: + return "Out of heap"; + case MP_VAL: + return "Value out of range"; + case MP_ITER: + return "Max. iterations reached"; + case MP_BUF: + return "Buffer overflow"; + case MP_OVF: + return "Integer overflow"; + default: + return "Invalid error code"; + } +} + +#endif diff --git a/src/libtommath/mp_exch.c b/src/libtommath/mp_exch.c new file mode 100644 index 000000000..50b97d963 --- /dev/null +++ b/src/libtommath/mp_exch.c @@ -0,0 +1,13 @@ +#include "tommath_private.h" +#ifdef MP_EXCH_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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_EXCH(mp_int, *a, *b); +} +#endif diff --git a/src/libtommath/mp_expt_n.c b/src/libtommath/mp_expt_n.c new file mode 100644 index 000000000..93f9249a7 --- /dev/null +++ b/src/libtommath/mp_expt_n.c @@ -0,0 +1,43 @@ +#include "tommath_private.h" +#ifdef MP_EXPT_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* calculate c = a**b using a square-multiply algorithm */ +mp_err mp_expt_n(const mp_int *a, int b, mp_int *c) +{ + mp_err err; + mp_int g; + + if ((err = mp_init_copy(&g, a)) != MP_OKAY) { + return err; + } + + /* set initial result */ + mp_set(c, 1uL); + + while (b > 0) { + /* if the bit is set multiply */ + if ((b & 1) != 0) { + if ((err = mp_mul(c, &g, c)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* square */ + if (b > 1) { + if ((err = mp_sqr(&g, &g)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* shift to next bit */ + b >>= 1; + } + +LBL_ERR: + mp_clear(&g); + return err; +} + +#endif diff --git a/src/libtommath/mp_exptmod.c b/src/libtommath/mp_exptmod.c new file mode 100644 index 000000000..b8a5dccc2 --- /dev/null +++ b/src/libtommath/mp_exptmod.c @@ -0,0 +1,78 @@ +#include "tommath_private.h" +#ifdef MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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) + */ +mp_err mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y) +{ + int dr; + + /* modulus P must be positive */ + if (mp_isneg(P)) { + return MP_VAL; + } + + /* if exponent X is negative we have to recurse */ + if (mp_isneg(X)) { + mp_int tmpG, tmpX; + mp_err err; + + if (!MP_HAS(MP_INVMOD)) { + return MP_VAL; + } + + if ((err = mp_init_multi(&tmpG, &tmpX, NULL)) != MP_OKAY) { + return err; + } + + /* first compute 1/G mod P */ + if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { + goto LBL_ERR; + } + + /* now get |X| */ + if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { + goto LBL_ERR; + } + + /* and now compute (1/G)**|X| instead of G**X [X < 0] */ + err = mp_exptmod(&tmpG, &tmpX, P, Y); +LBL_ERR: + mp_clear_multi(&tmpG, &tmpX, NULL); + return err; + } + + /* modified diminished radix reduction */ + if (MP_HAS(MP_REDUCE_IS_2K_L) && MP_HAS(MP_REDUCE_2K_L) && MP_HAS(S_MP_EXPTMOD) && + mp_reduce_is_2k_l(P)) { + return s_mp_exptmod(G, X, P, Y, 1); + } + + /* is it a DR modulus? default to no */ + dr = (MP_HAS(MP_DR_IS_MODULUS) && mp_dr_is_modulus(P)) ? 1 : 0; + + /* if not, is it a unrestricted DR modulus? */ + if (MP_HAS(MP_REDUCE_IS_2K) && (dr == 0)) { + dr = (mp_reduce_is_2k(P)) ? 2 : 0; + } + + /* if the modulus is odd or dr != 0 use the montgomery method */ + if (MP_HAS(S_MP_EXPTMOD_FAST) && (mp_isodd(P) || (dr != 0))) { + return s_mp_exptmod_fast(G, X, P, Y, dr); + } + + /* otherwise use the generic Barrett reduction technique */ + if (MP_HAS(S_MP_EXPTMOD)) { + return s_mp_exptmod(G, X, P, Y, 0); + } + + /* no exptmod for evens */ + return MP_VAL; +} + +#endif diff --git a/src/libtommath/mp_exteuclid.c b/src/libtommath/mp_exteuclid.c new file mode 100644 index 000000000..649c4ca85 --- /dev/null +++ b/src/libtommath/mp_exteuclid.c @@ -0,0 +1,72 @@ +#include "tommath_private.h" +#ifdef MP_EXTEUCLID_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* Extended euclidean algorithm of (a, b) produces + a*u1 + b*u2 = u3 + */ +mp_err mp_exteuclid(const mp_int *a, const 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; + mp_err 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, 1uL); + if ((err = mp_copy(a, &u3)) != MP_OKAY) goto LBL_ERR; + + /* initialize, (v1,v2,v3) = (0,1,b) */ + mp_set(&v2, 1uL); + if ((err = mp_copy(b, &v3)) != MP_OKAY) goto LBL_ERR; + + /* loop while v3 != 0 */ + while (!mp_iszero(&v3)) { + /* q = u3/v3 */ + if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) goto LBL_ERR; + + /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ + if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) goto LBL_ERR; + + /* (u1,u2,u3) = (v1,v2,v3) */ + if ((err = mp_copy(&v1, &u1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_copy(&v2, &u2)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_copy(&v3, &u3)) != MP_OKAY) goto LBL_ERR; + + /* (v1,v2,v3) = (t1,t2,t3) */ + if ((err = mp_copy(&t1, &v1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_copy(&t2, &v2)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_copy(&t3, &v3)) != MP_OKAY) goto LBL_ERR; + } + + /* make sure U3 >= 0 */ + if (mp_isneg(&u3)) { + if ((err = mp_neg(&u1, &u1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_neg(&u2, &u2)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_neg(&u3, &u3)) != MP_OKAY) goto LBL_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); + } + +LBL_ERR: + mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); + return err; +} +#endif diff --git a/src/libtommath/mp_fread.c b/src/libtommath/mp_fread.c new file mode 100644 index 000000000..53c35e822 --- /dev/null +++ b/src/libtommath/mp_fread.c @@ -0,0 +1,66 @@ +#include "tommath_private.h" +#ifdef MP_FREAD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifndef MP_NO_FILE +/* read a bigint from a file stream in ASCII */ +mp_err mp_fread(mp_int *a, int radix, FILE *stream) +{ + mp_err err; + mp_sign sign = MP_ZPOS; + int ch; + + /* make sure the radix is ok */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + /* if first digit is - then set negative */ + ch = fgetc(stream); + if (ch == (int)'-') { + sign = MP_NEG; + ch = fgetc(stream); + } + + /* no digits, return error */ + if (ch == EOF) { + return MP_ERR; + } + + /* clear a */ + mp_zero(a); + + do { + uint8_t y; + unsigned pos; + ch = (radix <= 36) ? MP_TOUPPER(ch) : ch; + pos = (unsigned)(ch - (int)'+'); + if (MP_RADIX_MAP_REVERSE_SIZE <= pos) { + break; + } + + y = s_mp_radix_map_reverse[pos]; + + if (y >= radix) { + break; + } + + /* shift up and add */ + if ((err = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + } while ((ch = fgetc(stream)) != EOF); + + if (!mp_iszero(a)) { + a->sign = sign; + } + + return MP_OKAY; +} +#endif + +#endif diff --git a/src/libtommath/mp_from_sbin.c b/src/libtommath/mp_from_sbin.c new file mode 100644 index 000000000..26eb0f120 --- /dev/null +++ b/src/libtommath/mp_from_sbin.c @@ -0,0 +1,21 @@ +#include "tommath_private.h" +#ifdef MP_FROM_SBIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* read signed bin, big endian, first byte is 0==positive or 1==negative */ +mp_err mp_from_sbin(mp_int *a, const uint8_t *buf, size_t size) +{ + mp_err err; + + /* read magnitude */ + if ((err = mp_from_ubin(a, buf + 1, size - 1u)) != MP_OKAY) { + return err; + } + + /* first byte is 0 for positive, non-zero for negative */ + a->sign = (buf[0] != (uint8_t)0) ? MP_NEG : MP_ZPOS; + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_from_ubin.c b/src/libtommath/mp_from_ubin.c new file mode 100644 index 000000000..8272185b8 --- /dev/null +++ b/src/libtommath/mp_from_ubin.c @@ -0,0 +1,30 @@ +#include "tommath_private.h" +#ifdef MP_FROM_UBIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* reads a uint8_t array, assumes the msb is stored first [big endian] */ +mp_err mp_from_ubin(mp_int *a, const uint8_t *buf, size_t size) +{ + mp_err err; + + /* make sure there are at least two digits */ + if ((err = mp_grow(a, 2)) != MP_OKAY) { + return err; + } + + /* zero the int */ + mp_zero(a); + + /* read the bytes in */ + while (size-- > 0u) { + if ((err = mp_mul_2d(a, 8, a)) != MP_OKAY) { + return err; + } + a->dp[0] |= *buf++; + a->used += 1; + } + mp_clamp(a); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_fwrite.c b/src/libtommath/mp_fwrite.c new file mode 100644 index 000000000..3d8141f06 --- /dev/null +++ b/src/libtommath/mp_fwrite.c @@ -0,0 +1,33 @@ +#include "tommath_private.h" +#ifdef MP_FWRITE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifndef MP_NO_FILE +mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream) +{ + char *buf; + mp_err err; + size_t size, written; + + if ((err = mp_radix_size_overestimate(a, radix, &size)) != MP_OKAY) { + return err; + } + + buf = (char *) MP_MALLOC(size); + if (buf == NULL) { + return MP_MEM; + } + + if ((err = mp_to_radix(a, buf, size, &written, radix)) == MP_OKAY) { + if (fwrite(buf, written, 1uL, stream) != 1uL) { + err = MP_ERR; + } + } + + MP_FREE_BUF(buf, size); + return err; +} +#endif + +#endif diff --git a/src/libtommath/mp_gcd.c b/src/libtommath/mp_gcd.c new file mode 100644 index 000000000..4f6b6cc29 --- /dev/null +++ b/src/libtommath/mp_gcd.c @@ -0,0 +1,92 @@ +#include "tommath_private.h" +#ifdef MP_GCD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* Greatest Common Divisor using the binary method */ +mp_err mp_gcd(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_int u, v; + int k, u_lsb, v_lsb; + mp_err err; + + /* either zero than gcd is the largest */ + if (mp_iszero(a)) { + return mp_abs(b, c); + } + if (mp_iszero(b)) { + return mp_abs(a, c); + } + + /* get copies of a and b we can modify */ + if ((err = mp_init_copy(&u, a)) != MP_OKAY) { + return err; + } + + if ((err = mp_init_copy(&v, b)) != MP_OKAY) { + goto LBL_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 = MP_MIN(u_lsb, v_lsb); + + if (k > 0) { + /* divide the power of two out */ + if ((err = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + + if ((err = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* divide any remaining factors of two out */ + if (u_lsb != k) { + if ((err = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + if (v_lsb != k) { + if ((err = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + while (!mp_iszero(&v)) { + /* 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 ((err = s_mp_sub(&v, &u, &v)) != MP_OKAY) { + goto LBL_V; + } + + /* Divide out all factors of two */ + if ((err = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { + goto LBL_V; + } + } + + /* multiply by 2**k which we divided out at the beginning */ + if ((err = mp_mul_2d(&u, k, c)) != MP_OKAY) { + goto LBL_V; + } + c->sign = MP_ZPOS; + err = MP_OKAY; +LBL_V: + mp_clear(&u); +LBL_U: + mp_clear(&v); + return err; +} +#endif diff --git a/src/libtommath/mp_get_double.c b/src/libtommath/mp_get_double.c new file mode 100644 index 000000000..f462eb8e0 --- /dev/null +++ b/src/libtommath/mp_get_double.c @@ -0,0 +1,18 @@ +#include "tommath_private.h" +#ifdef MP_GET_DOUBLE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +double mp_get_double(const mp_int *a) +{ + int i; + double d = 0.0, fac = 1.0; + for (i = 0; i < MP_DIGIT_BIT; ++i) { + fac *= 2.0; + } + for (i = a->used; i --> 0;) { + d = (d * fac) + (double)a->dp[i]; + } + return mp_isneg(a) ? -d : d; +} +#endif diff --git a/src/libtommath/mp_get_i32.c b/src/libtommath/mp_get_i32.c new file mode 100644 index 000000000..6b3b6addf --- /dev/null +++ b/src/libtommath/mp_get_i32.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_GET_I32_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_GET_SIGNED(mp_get_i32, mp_get_mag_u32, int32_t, uint32_t) +#endif diff --git a/src/libtommath/mp_get_i64.c b/src/libtommath/mp_get_i64.c new file mode 100644 index 000000000..8d38a1f25 --- /dev/null +++ b/src/libtommath/mp_get_i64.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_GET_I64_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_GET_SIGNED(mp_get_i64, mp_get_mag_u64, int64_t, uint64_t) +#endif diff --git a/src/libtommath/mp_get_l.c b/src/libtommath/mp_get_l.c new file mode 100644 index 000000000..3a1a2f7bc --- /dev/null +++ b/src/libtommath/mp_get_l.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_GET_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_GET_SIGNED(mp_get_l, mp_get_mag_ul, long, unsigned long) +#endif diff --git a/src/libtommath/mp_get_mag_u32.c b/src/libtommath/mp_get_mag_u32.c new file mode 100644 index 000000000..acddc5872 --- /dev/null +++ b/src/libtommath/mp_get_mag_u32.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_GET_MAG_U32_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_GET_MAG(mp_get_mag_u32, uint32_t) +#endif diff --git a/src/libtommath/mp_get_mag_u64.c b/src/libtommath/mp_get_mag_u64.c new file mode 100644 index 000000000..f75463929 --- /dev/null +++ b/src/libtommath/mp_get_mag_u64.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_GET_MAG_U64_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_GET_MAG(mp_get_mag_u64, uint64_t) +#endif diff --git a/src/libtommath/mp_get_mag_ul.c b/src/libtommath/mp_get_mag_ul.c new file mode 100644 index 000000000..5c6043052 --- /dev/null +++ b/src/libtommath/mp_get_mag_ul.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_GET_MAG_UL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_GET_MAG(mp_get_mag_ul, unsigned long) +#endif diff --git a/src/libtommath/mp_grow.c b/src/libtommath/mp_grow.c new file mode 100644 index 000000000..5bca1b5ff --- /dev/null +++ b/src/libtommath/mp_grow.c @@ -0,0 +1,40 @@ +#include "tommath_private.h" +#ifdef MP_GROW_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* grow as required */ +mp_err mp_grow(mp_int *a, int size) +{ + /* if the alloc size is smaller alloc more ram */ + if (a->alloc < size) { + mp_digit *dp; + + if (size > MP_MAX_DIGIT_COUNT) { + return MP_OVF; + } + + /* 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. + */ + dp = (mp_digit *) MP_REALLOC(a->dp, + (size_t)a->alloc * sizeof(mp_digit), + (size_t)size * sizeof(mp_digit)); + if (dp == NULL) { + /* reallocation failed but "a" is still valid [can be freed] */ + return MP_MEM; + } + + /* reallocation succeeded so set a->dp */ + a->dp = dp; + + /* zero excess digits */ + s_mp_zero_digs(a->dp + a->alloc, size - a->alloc); + a->alloc = size; + } + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_init.c b/src/libtommath/mp_init.c new file mode 100644 index 000000000..af1674481 --- /dev/null +++ b/src/libtommath/mp_init.c @@ -0,0 +1,23 @@ +#include "tommath_private.h" +#ifdef MP_INIT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* init a new mp_int */ +mp_err mp_init(mp_int *a) +{ + /* allocate memory required and clear it */ + a->dp = (mp_digit *) MP_CALLOC((size_t)MP_DEFAULT_DIGIT_COUNT, sizeof(mp_digit)); + 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_DEFAULT_DIGIT_COUNT; + a->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_init_copy.c b/src/libtommath/mp_init_copy.c new file mode 100644 index 000000000..4d0773bad --- /dev/null +++ b/src/libtommath/mp_init_copy.c @@ -0,0 +1,21 @@ +#include "tommath_private.h" +#ifdef MP_INIT_COPY_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* creates "a" then copies b into it */ +mp_err mp_init_copy(mp_int *a, const mp_int *b) +{ + mp_err err; + + if ((err = mp_init_size(a, b->used)) != MP_OKAY) { + return err; + } + + if ((err = mp_copy(b, a)) != MP_OKAY) { + mp_clear(a); + } + + return err; +} +#endif diff --git a/src/libtommath/mp_init_i32.c b/src/libtommath/mp_init_i32.c new file mode 100644 index 000000000..434788f8c --- /dev/null +++ b/src/libtommath/mp_init_i32.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_INIT_I32_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_INIT_INT(mp_init_i32, mp_set_i32, int32_t) +#endif diff --git a/src/libtommath/mp_init_i64.c b/src/libtommath/mp_init_i64.c new file mode 100644 index 000000000..718567809 --- /dev/null +++ b/src/libtommath/mp_init_i64.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_INIT_I64_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_INIT_INT(mp_init_i64, mp_set_i64, int64_t) +#endif diff --git a/src/libtommath/mp_init_l.c b/src/libtommath/mp_init_l.c new file mode 100644 index 000000000..16be8f63f --- /dev/null +++ b/src/libtommath/mp_init_l.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_INIT_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_INIT_INT(mp_init_l, mp_set_l, long) +#endif diff --git a/src/libtommath/mp_init_multi.c b/src/libtommath/mp_init_multi.c new file mode 100644 index 000000000..908b4df45 --- /dev/null +++ b/src/libtommath/mp_init_multi.c @@ -0,0 +1,41 @@ +#include "tommath_private.h" +#ifdef MP_INIT_MULTI_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include + +mp_err mp_init_multi(mp_int *mp, ...) +{ + mp_err err = MP_OKAY; + 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) { + err = mp_init(cur_arg); + if (err != MP_OKAY) { + /* Oops - error! Back-track and mp_clear what we already + succeeded in init-ing, then return error. + */ + va_list clean_args; + + /* now start cleaning up */ + cur_arg = mp; + va_start(clean_args, mp); + while (n-- != 0) { + mp_clear(cur_arg); + cur_arg = va_arg(clean_args, mp_int *); + } + va_end(clean_args); + break; + } + n++; + cur_arg = va_arg(args, mp_int *); + } + va_end(args); + return err; +} + +#endif diff --git a/src/libtommath/mp_init_set.c b/src/libtommath/mp_init_set.c new file mode 100644 index 000000000..e1f2ee94d --- /dev/null +++ b/src/libtommath/mp_init_set.c @@ -0,0 +1,16 @@ +#include "tommath_private.h" +#ifdef MP_INIT_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* initialize and set a digit */ +mp_err mp_init_set(mp_int *a, mp_digit b) +{ + mp_err err; + if ((err = mp_init(a)) != MP_OKAY) { + return err; + } + mp_set(a, b); + return err; +} +#endif diff --git a/src/libtommath/mp_init_size.c b/src/libtommath/mp_init_size.c new file mode 100644 index 000000000..e28a3cd49 --- /dev/null +++ b/src/libtommath/mp_init_size.c @@ -0,0 +1,28 @@ +#include "tommath_private.h" +#ifdef MP_INIT_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* init an mp_init for a given size */ +mp_err mp_init_size(mp_int *a, int size) +{ + size = MP_MAX(MP_MIN_DIGIT_COUNT, size); + + if (size > MP_MAX_DIGIT_COUNT) { + return MP_OVF; + } + + /* alloc mem */ + a->dp = (mp_digit *) MP_CALLOC((size_t)size, sizeof(mp_digit)); + if (a->dp == NULL) { + return MP_MEM; + } + + /* set the members */ + a->used = 0; + a->alloc = size; + a->sign = MP_ZPOS; + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_init_u32.c b/src/libtommath/mp_init_u32.c new file mode 100644 index 000000000..d5a2b8f82 --- /dev/null +++ b/src/libtommath/mp_init_u32.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_INIT_U32_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_INIT_INT(mp_init_u32, mp_set_u32, uint32_t) +#endif diff --git a/src/libtommath/mp_init_u64.c b/src/libtommath/mp_init_u64.c new file mode 100644 index 000000000..ca7508405 --- /dev/null +++ b/src/libtommath/mp_init_u64.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_INIT_U64_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_INIT_INT(mp_init_u64, mp_set_u64, uint64_t) +#endif diff --git a/src/libtommath/mp_init_ul.c b/src/libtommath/mp_init_ul.c new file mode 100644 index 000000000..21ca3be51 --- /dev/null +++ b/src/libtommath/mp_init_ul.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_INIT_UL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_INIT_INT(mp_init_ul, mp_set_ul, unsigned long) +#endif diff --git a/src/libtommath/mp_invmod.c b/src/libtommath/mp_invmod.c new file mode 100644 index 000000000..2494acbf6 --- /dev/null +++ b/src/libtommath/mp_invmod.c @@ -0,0 +1,29 @@ +#include "tommath_private.h" +#ifdef MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* hac 14.61, pp608 */ +mp_err mp_invmod(const mp_int *a, const mp_int *b, mp_int *c) +{ + /* for all n in N and n > 0, n = 0 mod 1 */ + if (!mp_isneg(a) && mp_cmp_d(b, 1uL) == MP_EQ) { + mp_zero(c); + return MP_OKAY; + } + + /* b cannot be negative and has to be >1 */ + if (mp_isneg(b) || (mp_cmp_d(b, 1uL) != MP_GT)) { + return MP_VAL; + } + + /* if the modulus is odd we can use a faster routine instead */ + if (MP_HAS(S_MP_INVMOD_ODD) && mp_isodd(b)) { + return s_mp_invmod_odd(a, b, c); + } + + return MP_HAS(S_MP_INVMOD) + ? s_mp_invmod(a, b, c) + : MP_VAL; +} +#endif diff --git a/src/libtommath/mp_is_square.c b/src/libtommath/mp_is_square.c new file mode 100644 index 000000000..db1cb3fb3 --- /dev/null +++ b/src/libtommath/mp_is_square.c @@ -0,0 +1,93 @@ +#include "tommath_private.h" +#ifdef MP_IS_SQUARE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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 */ +mp_err mp_is_square(const mp_int *arg, bool *ret) +{ + mp_err err; + mp_digit c; + mp_int t; + uint32_t r; + + /* Default to Non-square :) */ + *ret = false; + + if (mp_isneg(arg)) { + return MP_VAL; + } + + if (mp_iszero(arg)) { + return MP_OKAY; + } + + /* First check mod 128 (suppose that MP_DIGIT_BIT is at least 7) */ + if (rem_128[127u & arg->dp[0]] == (char)1) { + return MP_OKAY; + } + + /* Next check mod 105 (3*5*7) */ + if ((err = mp_mod_d(arg, 105uL, &c)) != MP_OKAY) { + return err; + } + if (rem_105[c] == (char)1) { + return MP_OKAY; + } + + + if ((err = mp_init_u32(&t, 11u*13u*17u*19u*23u*29u*31u)) != MP_OKAY) { + return err; + } + if ((err = mp_mod(arg, &t, &t)) != MP_OKAY) { + goto LBL_ERR; + } + r = mp_get_u32(&t); + /* Check for other prime modules, note it's not an ERROR but we must + * free "t" so the easiest way is to goto LBL_ERR. We know that err + * is already equal to MP_OKAY from the mp_mod call + */ + if (((1uL<<(r%11uL)) & 0x5C4uL) != 0uL) goto LBL_ERR; + if (((1uL<<(r%13uL)) & 0x9E4uL) != 0uL) goto LBL_ERR; + if (((1uL<<(r%17uL)) & 0x5CE8uL) != 0uL) goto LBL_ERR; + if (((1uL<<(r%19uL)) & 0x4F50CuL) != 0uL) goto LBL_ERR; + if (((1uL<<(r%23uL)) & 0x7ACCA0uL) != 0uL) goto LBL_ERR; + if (((1uL<<(r%29uL)) & 0xC2EDD0CuL) != 0uL) goto LBL_ERR; + if (((1uL<<(r%31uL)) & 0x6DE2B848uL) != 0uL) goto LBL_ERR; + + /* Final check - is sqr(sqrt(arg)) == arg ? */ + if ((err = mp_sqrt(arg, &t)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_sqr(&t, &t)) != MP_OKAY) { + goto LBL_ERR; + } + + *ret = (mp_cmp_mag(&t, arg) == MP_EQ); +LBL_ERR: + mp_clear(&t); + return err; +} +#endif diff --git a/src/libtommath/mp_kronecker.c b/src/libtommath/mp_kronecker.c new file mode 100644 index 000000000..e6bedc891 --- /dev/null +++ b/src/libtommath/mp_kronecker.c @@ -0,0 +1,129 @@ +#include "tommath_private.h" +#ifdef MP_KRONECKER_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* + Kronecker symbol (a|p) + Straightforward implementation of algorithm 1.4.10 in + Henri Cohen: "A Course in Computational Algebraic Number Theory" + + @book{cohen2013course, + title={A course in computational algebraic number theory}, + author={Cohen, Henri}, + volume={138}, + year={2013}, + publisher={Springer Science \& Business Media} + } + */ +mp_err mp_kronecker(const mp_int *a, const mp_int *p, int *c) +{ + mp_int a1, p1, r; + mp_err err; + int v, k; + + static const char table[] = {0, 1, 0, -1, 0, -1, 0, 1}; + + if (mp_iszero(p)) { + if ((a->used == 1) && (a->dp[0] == 1u)) { + *c = 1; + } else { + *c = 0; + } + return MP_OKAY; + } + + if (mp_iseven(a) && mp_iseven(p)) { + *c = 0; + return MP_OKAY; + } + + if ((err = mp_init_copy(&a1, a)) != MP_OKAY) { + return err; + } + if ((err = mp_init_copy(&p1, p)) != MP_OKAY) { + goto LBL_KRON_0; + } + + v = mp_cnt_lsb(&p1); + if ((err = mp_div_2d(&p1, v, &p1, NULL)) != MP_OKAY) { + goto LBL_KRON_1; + } + + if ((v & 1) == 0) { + k = 1; + } else { + k = table[a->dp[0] & 7u]; + } + + if (mp_isneg(&p1)) { + p1.sign = MP_ZPOS; + if (mp_isneg(&a1)) { + k = -k; + } + } + + if ((err = mp_init(&r)) != MP_OKAY) { + goto LBL_KRON_1; + } + + for (;;) { + if (mp_iszero(&a1)) { + if (mp_cmp_d(&p1, 1uL) == MP_EQ) { + *c = k; + goto LBL_KRON; + } else { + *c = 0; + goto LBL_KRON; + } + } + + v = mp_cnt_lsb(&a1); + if ((err = mp_div_2d(&a1, v, &a1, NULL)) != MP_OKAY) { + goto LBL_KRON; + } + + if ((v & 1) == 1) { + k = k * table[p1.dp[0] & 7u]; + } + + if (mp_isneg(&a1)) { + /* + * Compute k = (-1)^((a1)*(p1-1)/4) * k + * a1.dp[0] + 1 cannot overflow because the MSB + * of the type mp_digit is not set by definition + */ + if (((a1.dp[0] + 1u) & p1.dp[0] & 2u) != 0u) { + k = -k; + } + } else { + /* compute k = (-1)^((a1-1)*(p1-1)/4) * k */ + if ((a1.dp[0] & p1.dp[0] & 2u) != 0u) { + k = -k; + } + } + + if ((err = mp_copy(&a1, &r)) != MP_OKAY) { + goto LBL_KRON; + } + r.sign = MP_ZPOS; + if ((err = mp_mod(&p1, &r, &a1)) != MP_OKAY) { + goto LBL_KRON; + } + if ((err = mp_copy(&r, &p1)) != MP_OKAY) { + goto LBL_KRON; + } + } + +LBL_KRON: + mp_clear(&r); +LBL_KRON_1: + mp_clear(&p1); +LBL_KRON_0: + mp_clear(&a1); + + return err; +} + +#endif diff --git a/src/libtommath/mp_lcm.c b/src/libtommath/mp_lcm.c new file mode 100644 index 000000000..f2044f0e5 --- /dev/null +++ b/src/libtommath/mp_lcm.c @@ -0,0 +1,44 @@ +#include "tommath_private.h" +#ifdef MP_LCM_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* computes least common multiple as |a*b|/(a, b) */ +mp_err mp_lcm(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_err err; + mp_int t1, t2; + + + if ((err = mp_init_multi(&t1, &t2, NULL)) != MP_OKAY) { + return err; + } + + /* t1 = get the GCD of the two inputs */ + if ((err = mp_gcd(a, b, &t1)) != MP_OKAY) { + goto LBL_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 ((err = mp_div(a, &t1, &t2, NULL)) != MP_OKAY) { + goto LBL_T; + } + err = mp_mul(b, &t2, c); + } else { + /* store quotient in t2 such that t2 * a is the LCM */ + if ((err = mp_div(b, &t1, &t2, NULL)) != MP_OKAY) { + goto LBL_T; + } + err = mp_mul(a, &t2, c); + } + + /* fix the sign to positive */ + c->sign = MP_ZPOS; + +LBL_T: + mp_clear_multi(&t1, &t2, NULL); + return err; +} +#endif diff --git a/src/libtommath/mp_log_n.c b/src/libtommath/mp_log_n.c new file mode 100644 index 000000000..4de1e3993 --- /dev/null +++ b/src/libtommath/mp_log_n.c @@ -0,0 +1,29 @@ +#include "tommath_private.h" +#ifdef MP_LOG_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +mp_err mp_log_n(const mp_int *a, int base, int *c) +{ + if (mp_isneg(a) || mp_iszero(a) || (base < 2) || (unsigned)base > (unsigned)MP_DIGIT_MAX) { + return MP_VAL; + } + + if (MP_HAS(S_MP_LOG_2EXPT) && MP_IS_2EXPT((mp_digit)base)) { + *c = s_mp_log_2expt(a, (mp_digit)base); + return MP_OKAY; + } + + if (MP_HAS(S_MP_LOG_D) && (a->used == 1)) { + *c = s_mp_log_d((mp_digit)base, a->dp[0]); + return MP_OKAY; + } + + if (MP_HAS(S_MP_LOG)) { + return s_mp_log(a, (mp_digit)base, c); + } + + return MP_VAL; +} + +#endif diff --git a/src/libtommath/mp_lshd.c b/src/libtommath/mp_lshd.c new file mode 100644 index 000000000..bfa8af88b --- /dev/null +++ b/src/libtommath/mp_lshd.c @@ -0,0 +1,42 @@ +#include "tommath_private.h" +#ifdef MP_LSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* shift left a certain amount of digits */ +mp_err mp_lshd(mp_int *a, int b) +{ + mp_err err; + int x; + + /* if its less than zero return */ + if (b <= 0) { + return MP_OKAY; + } + /* no need to shift 0 around */ + if (mp_iszero(a)) { + return MP_OKAY; + } + + /* grow to fit the new digits */ + if ((err = mp_grow(a, a->used + b)) != MP_OKAY) { + return err; + } + + /* increment the used by the shift amount then copy upwards */ + a->used += 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 mp_rshd.c for more info. + */ + for (x = a->used; x --> b;) { + a->dp[x] = a->dp[x - b]; + } + + /* zero the lower digits */ + s_mp_zero_digs(a->dp, b); + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_mod.c b/src/libtommath/mp_mod.c new file mode 100644 index 000000000..beae13e72 --- /dev/null +++ b/src/libtommath/mp_mod.c @@ -0,0 +1,15 @@ +#include "tommath_private.h" +#ifdef MP_MOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */ +mp_err mp_mod(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_err err; + if ((err = mp_div(a, b, NULL, c)) != MP_OKAY) { + return err; + } + return mp_iszero(c) || (c->sign == b->sign) ? MP_OKAY : mp_add(b, c, c); +} +#endif diff --git a/src/libtommath/mp_mod_2d.c b/src/libtommath/mp_mod_2d.c new file mode 100644 index 000000000..82c64f05f --- /dev/null +++ b/src/libtommath/mp_mod_2d.c @@ -0,0 +1,40 @@ +#include "tommath_private.h" +#ifdef MP_MOD_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* calc a value mod 2**b */ +mp_err mp_mod_2d(const mp_int *a, int b, mp_int *c) +{ + int x; + mp_err err; + + if (b < 0) { + return MP_VAL; + } + + if (b == 0) { + mp_zero(c); + return MP_OKAY; + } + + /* if the modulus is larger than the value than return */ + if (b >= (a->used * MP_DIGIT_BIT)) { + return mp_copy(a, c); + } + + if ((err = mp_copy(a, c)) != MP_OKAY) { + return err; + } + + /* zero digits above the last digit of the modulus */ + x = (b / MP_DIGIT_BIT) + (((b % MP_DIGIT_BIT) == 0) ? 0 : 1); + s_mp_zero_digs(c->dp + x, c->used - x); + + /* clear the digit that is not completely outside/inside the modulus */ + c->dp[b / MP_DIGIT_BIT] &= + ((mp_digit)1 << (mp_digit)(b % MP_DIGIT_BIT)) - (mp_digit)1; + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_montgomery_calc_normalization.c b/src/libtommath/mp_montgomery_calc_normalization.c new file mode 100644 index 000000000..cc07799dc --- /dev/null +++ b/src/libtommath/mp_montgomery_calc_normalization.c @@ -0,0 +1,43 @@ +#include "tommath_private.h" +#ifdef MP_MONTGOMERY_CALC_NORMALIZATION_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* + * 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. + */ +mp_err mp_montgomery_calc_normalization(mp_int *a, const mp_int *b) +{ + int x, bits; + mp_err err; + + /* how many bits of last digit does b use */ + bits = mp_count_bits(b) % MP_DIGIT_BIT; + + if (b->used > 1) { + if ((err = mp_2expt(a, ((b->used - 1) * MP_DIGIT_BIT) + bits - 1)) != MP_OKAY) { + return err; + } + } else { + mp_set(a, 1uL); + bits = 1; + } + + /* now compute C = A * B mod b */ + for (x = bits - 1; x < (int)MP_DIGIT_BIT; x++) { + if ((err = mp_mul_2(a, a)) != MP_OKAY) { + return err; + } + if (mp_cmp_mag(a, b) != MP_LT) { + if ((err = s_mp_sub(a, b, a)) != MP_OKAY) { + return err; + } + } + } + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_montgomery_reduce.c b/src/libtommath/mp_montgomery_reduce.c new file mode 100644 index 000000000..dbf45d3cf --- /dev/null +++ b/src/libtommath/mp_montgomery_reduce.c @@ -0,0 +1,89 @@ +#include "tommath_private.h" +#ifdef MP_MONTGOMERY_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction */ +mp_err mp_montgomery_reduce(mp_int *x, const mp_int *n, mp_digit rho) +{ + mp_err err; + int ix, digs; + + /* can the fast reduction [comba] method be used? + * + * Note that unlike in 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) && + (x->used <= MP_WARRAY) && + (n->used < MP_MAX_COMBA)) { + return s_mp_montgomery_reduce_comba(x, n, rho); + } + + /* grow the input as required */ + if ((err = mp_grow(x, digs)) != MP_OKAY) { + return err; + } + x->used = digs; + + for (ix = 0; ix < n->used; ix++) { + int iy; + mp_digit u, mu; + + /* mu = ai * rho mod b + * + * The value of rho must be precalculated via + * 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 */ + + /* Multiply and add in place */ + u = 0; + for (iy = 0; iy < n->used; iy++) { + /* compute product and sum */ + mp_word r = ((mp_word)mu * (mp_word)n->dp[iy]) + + (mp_word)u + (mp_word)x->dp[ix + iy]; + + /* get carry */ + u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + + /* fix digit */ + x->dp[ix + iy] = (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 != 0u) { + x->dp[ix + iy] += u; + u = x->dp[ix + iy] >> MP_DIGIT_BIT; + x->dp[ix + iy] &= MP_MASK; + ++iy; + } + } + + /* 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; +} +#endif diff --git a/src/libtommath/mp_montgomery_setup.c b/src/libtommath/mp_montgomery_setup.c new file mode 100644 index 000000000..de57dc342 --- /dev/null +++ b/src/libtommath/mp_montgomery_setup.c @@ -0,0 +1,40 @@ +#include "tommath_private.h" +#ifdef MP_MONTGOMERY_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* setups the montgomery reduction stuff */ +mp_err mp_montgomery_setup(const 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 & 1u) == 0u) { + return MP_VAL; + } + + x = (((b + 2u) & 4u) << 1) + b; /* here x*a==1 mod 2**4 */ + x *= 2u - (b * x); /* here x*a==1 mod 2**8 */ + x *= 2u - (b * x); /* here x*a==1 mod 2**16 */ +#if defined(MP_64BIT) || !(defined(MP_16BIT)) + x *= 2u - (b * x); /* here x*a==1 mod 2**32 */ +#endif +#ifdef MP_64BIT + x *= 2u - (b * x); /* here x*a==1 mod 2**64 */ +#endif + + /* rho = -1/m mod b */ + *rho = (mp_digit)(((mp_word)1 << (mp_word)MP_DIGIT_BIT) - x) & MP_MASK; + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_mul.c b/src/libtommath/mp_mul.c new file mode 100644 index 000000000..d35fa8ef4 --- /dev/null +++ b/src/libtommath/mp_mul.c @@ -0,0 +1,68 @@ +#include "tommath_private.h" +#ifdef MP_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* high level multiplication (handles sign) */ +mp_err mp_mul(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_err err; + int min = MP_MIN(a->used, b->used), + max = MP_MAX(a->used, b->used), + digs = a->used + b->used + 1; + bool neg = (a->sign != b->sign); + + if ((a == b) && + MP_HAS(S_MP_SQR_TOOM) && /* use Toom-Cook? */ + (a->used >= MP_SQR_TOOM_CUTOFF)) { + err = s_mp_sqr_toom(a, c); + } else if ((a == b) && + MP_HAS(S_MP_SQR_KARATSUBA) && /* Karatsuba? */ + (a->used >= MP_SQR_KARATSUBA_CUTOFF)) { + err = s_mp_sqr_karatsuba(a, c); + } else if ((a == b) && + MP_HAS(S_MP_SQR_COMBA) && /* can we use the fast comba multiplier? */ + (((a->used * 2) + 1) < MP_WARRAY) && + (a->used < (MP_MAX_COMBA / 2))) { + err = s_mp_sqr_comba(a, c); + } else if ((a == b) && + MP_HAS(S_MP_SQR)) { + err = s_mp_sqr(a, c); + } else if (MP_HAS(S_MP_MUL_BALANCE) && + /* Check sizes. The smaller one needs to be larger than the Karatsuba cut-off. + * The bigger one needs to be at least about one MP_MUL_KARATSUBA_CUTOFF bigger + * to make some sense, but it depends on architecture, OS, position of the + * stars... so YMMV. + * Using it to cut the input into slices small enough for s_mp_mul_comba + * was actually slower on the author's machine, but YMMV. + */ + (min >= MP_MUL_KARATSUBA_CUTOFF) && + ((max / 2) >= MP_MUL_KARATSUBA_CUTOFF) && + /* Not much effect was observed below a ratio of 1:2, but again: YMMV. */ + (max >= (2 * min))) { + err = s_mp_mul_balance(a,b,c); + } else if (MP_HAS(S_MP_MUL_TOOM) && + (min >= MP_MUL_TOOM_CUTOFF)) { + err = s_mp_mul_toom(a, b, c); + } else if (MP_HAS(S_MP_MUL_KARATSUBA) && + (min >= MP_MUL_KARATSUBA_CUTOFF)) { + err = s_mp_mul_karatsuba(a, b, c); + } else if (MP_HAS(S_MP_MUL_COMBA) && + /* 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 + */ + (digs < MP_WARRAY) && + (min <= MP_MAX_COMBA)) { + err = s_mp_mul_comba(a, b, c, digs); + } else if (MP_HAS(S_MP_MUL)) { + err = s_mp_mul(a, b, c, digs); + } else { + err = MP_VAL; + } + c->sign = ((c->used > 0) && neg) ? MP_NEG : MP_ZPOS; + return err; +} +#endif diff --git a/src/libtommath/mp_mul_2.c b/src/libtommath/mp_mul_2.c new file mode 100644 index 000000000..7d7084b31 --- /dev/null +++ b/src/libtommath/mp_mul_2.c @@ -0,0 +1,53 @@ +#include "tommath_private.h" +#ifdef MP_MUL_2_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* b = a*2 */ +mp_err mp_mul_2(const mp_int *a, mp_int *b) +{ + mp_err err; + int x, oldused; + mp_digit r; + + /* grow to accomodate result */ + if ((err = mp_grow(b, a->used + 1)) != MP_OKAY) { + return err; + } + + oldused = b->used; + b->used = a->used; + + /* 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 + */ + mp_digit rr = a->dp[x] >> (mp_digit)(MP_DIGIT_BIT - 1); + + /* now shift up this digit, add in the carry [from the previous] */ + b->dp[x] = ((a->dp[x] << 1uL) | r) & MP_MASK; + + /* copy the carry that would be from the source + * digit into the next iteration + */ + r = rr; + } + + /* new leading digit? */ + if (r != 0u) { + /* add a MSB which is always 1 at this point */ + b->dp[b->used++] = 1; + } + + /* now zero any excess digits on the destination + * that we didn't write to + */ + s_mp_zero_digs(b->dp + b->used, oldused - b->used); + + b->sign = a->sign; + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_mul_2d.c b/src/libtommath/mp_mul_2d.c new file mode 100644 index 000000000..e4581375b --- /dev/null +++ b/src/libtommath/mp_mul_2d.c @@ -0,0 +1,63 @@ +#include "tommath_private.h" +#ifdef MP_MUL_2D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* shift left by a certain bit count */ +mp_err mp_mul_2d(const mp_int *a, int b, mp_int *c) +{ + mp_err err; + + if (b < 0) { + return MP_VAL; + } + + if ((err = mp_copy(a, c)) != MP_OKAY) { + return err; + } + + if ((err = mp_grow(c, c->used + (b / MP_DIGIT_BIT) + 1)) != MP_OKAY) { + return err; + } + + /* shift by as many digits in the bit count */ + if (b >= MP_DIGIT_BIT) { + if ((err = mp_lshd(c, b / MP_DIGIT_BIT)) != MP_OKAY) { + return err; + } + } + + /* shift any bit count < MP_DIGIT_BIT */ + b %= MP_DIGIT_BIT; + if (b != 0u) { + mp_digit shift, mask, r; + int x; + + /* bitmask for carries */ + mask = ((mp_digit)1 << b) - (mp_digit)1; + + /* shift for msbs */ + shift = (mp_digit)(MP_DIGIT_BIT - b); + + /* carry */ + r = 0; + for (x = 0; x < c->used; x++) { + /* get the higher bits of the current word */ + mp_digit rr = (c->dp[x] >> shift) & mask; + + /* shift the current word and OR in the carry */ + c->dp[x] = ((c->dp[x] << b) | r) & MP_MASK; + + /* set the carry to the carry bits of the current word */ + r = rr; + } + + /* set final carry */ + if (r != 0u) { + c->dp[(c->used)++] = r; + } + } + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_mul_d.c b/src/libtommath/mp_mul_d.c new file mode 100644 index 000000000..258505543 --- /dev/null +++ b/src/libtommath/mp_mul_d.c @@ -0,0 +1,68 @@ +#include "tommath_private.h" +#ifdef MP_MUL_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* multiply by a digit */ +mp_err mp_mul_d(const mp_int *a, mp_digit b, mp_int *c) +{ + mp_digit u; + mp_err err; + int ix, oldused; + + if (b == 1u) { + return mp_copy(a, c); + } + + /* power of two ? */ + if (MP_HAS(MP_MUL_2) && (b == 2u)) { + return mp_mul_2(a, c); + } + if (MP_HAS(MP_MUL_2D) && MP_IS_2EXPT(b)) { + ix = 1; + while ((ix < MP_DIGIT_BIT) && (b != (((mp_digit)1)<used + 1)) != MP_OKAY) { + return err; + } + + /* get the original destinations used count */ + oldused = c->used; + + /* set the sign */ + c->sign = a->sign; + + /* zero carry */ + u = 0; + + /* compute columns */ + for (ix = 0; ix < a->used; ix++) { + /* compute product and carry sum for this term */ + mp_word r = (mp_word)u + ((mp_word)a->dp[ix] * (mp_word)b); + + /* mask off higher bits to get a single digit */ + c->dp[ix] = (mp_digit)(r & (mp_word)MP_MASK); + + /* send carry into next iteration */ + u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + } + + /* store final carry [if any] and increment ix offset */ + c->dp[ix] = u; + + /* set used count */ + c->used = a->used + 1; + + /* now zero digits above the top */ + s_mp_zero_digs(c->dp + c->used, oldused - c->used); + + mp_clamp(c); + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_mulmod.c b/src/libtommath/mp_mulmod.c new file mode 100644 index 000000000..e158693b9 --- /dev/null +++ b/src/libtommath/mp_mulmod.c @@ -0,0 +1,15 @@ +#include "tommath_private.h" +#ifdef MP_MULMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* d = a * b (mod c) */ +mp_err mp_mulmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d) +{ + mp_err err; + if ((err = mp_mul(a, b, d)) != MP_OKAY) { + return err; + } + return mp_mod(d, c, d); +} +#endif diff --git a/src/libtommath/mp_neg.c b/src/libtommath/mp_neg.c new file mode 100644 index 000000000..b445cd409 --- /dev/null +++ b/src/libtommath/mp_neg.c @@ -0,0 +1,18 @@ +#include "tommath_private.h" +#ifdef MP_NEG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* b = -a */ +mp_err mp_neg(const mp_int *a, mp_int *b) +{ + mp_err err; + if ((err = mp_copy(a, b)) != MP_OKAY) { + return err; + } + + b->sign = ((!mp_iszero(b) && !mp_isneg(b)) ? MP_NEG : MP_ZPOS); + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_or.c b/src/libtommath/mp_or.c new file mode 100644 index 000000000..15958524e --- /dev/null +++ b/src/libtommath/mp_or.c @@ -0,0 +1,54 @@ +#include "tommath_private.h" +#ifdef MP_OR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* two complement or */ +mp_err mp_or(const mp_int *a, const mp_int *b, mp_int *c) +{ + int used = MP_MAX(a->used, b->used) + 1, i; + mp_err err; + mp_digit ac = 1, bc = 1, cc = 1; + bool neg = (mp_isneg(a) || mp_isneg(b)); + + if ((err = mp_grow(c, used)) != MP_OKAY) { + return err; + } + + for (i = 0; i < used; i++) { + mp_digit x, y; + + /* convert to two complement if negative */ + if (mp_isneg(a)) { + ac += (i >= a->used) ? MP_MASK : (~a->dp[i] & MP_MASK); + x = ac & MP_MASK; + ac >>= MP_DIGIT_BIT; + } else { + x = (i >= a->used) ? 0uL : a->dp[i]; + } + + /* convert to two complement if negative */ + if (mp_isneg(b)) { + bc += (i >= b->used) ? MP_MASK : (~b->dp[i] & MP_MASK); + y = bc & MP_MASK; + bc >>= MP_DIGIT_BIT; + } else { + y = (i >= b->used) ? 0uL : b->dp[i]; + } + + c->dp[i] = x | y; + + /* convert to to sign-magnitude if negative */ + if (neg) { + cc += ~c->dp[i] & MP_MASK; + c->dp[i] = cc & MP_MASK; + cc >>= MP_DIGIT_BIT; + } + } + + c->used = used; + c->sign = (neg ? MP_NEG : MP_ZPOS); + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_pack.c b/src/libtommath/mp_pack.c new file mode 100644 index 000000000..447f1fdf9 --- /dev/null +++ b/src/libtommath/mp_pack.c @@ -0,0 +1,69 @@ +#include "tommath_private.h" +#ifdef MP_PACK_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* based on gmp's mpz_export. + * see http://gmplib.org/manual/Integer-Import-and-Export.html + */ +mp_err mp_pack(void *rop, size_t maxcount, size_t *written, mp_order order, size_t size, + mp_endian endian, size_t nails, const mp_int *op) +{ + mp_err err; + size_t odd_nails, nail_bytes, i, j, count; + uint8_t odd_nail_mask; + + mp_int t; + + count = mp_pack_count(op, nails, size); + + if (count > maxcount) { + return MP_BUF; + } + + if ((err = mp_init_copy(&t, op)) != MP_OKAY) { + return err; + } + + if (endian == MP_NATIVE_ENDIAN) { + MP_GET_ENDIANNESS(endian); + } + + odd_nails = (nails % 8u); + odd_nail_mask = 0xff; + for (i = 0u; i < odd_nails; ++i) { + odd_nail_mask ^= (uint8_t)(1u << (7u - i)); + } + nail_bytes = nails / 8u; + + for (i = 0u; i < count; ++i) { + for (j = 0u; j < size; ++j) { + uint8_t *byte = (uint8_t *)rop + + (((order == MP_LSB_FIRST) ? i : ((count - 1u) - i)) * size) + + ((endian == MP_LITTLE_ENDIAN) ? j : ((size - 1u) - j)); + + if (j >= (size - nail_bytes)) { + *byte = 0; + continue; + } + + *byte = (uint8_t)((j == ((size - nail_bytes) - 1u)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFFuL)); + + if ((err = mp_div_2d(&t, (j == ((size - nail_bytes) - 1u)) ? (int)(8u - odd_nails) : 8, &t, NULL)) != MP_OKAY) { + goto LBL_ERR; + } + + } + } + + if (written != NULL) { + *written = count; + } + err = MP_OKAY; + +LBL_ERR: + mp_clear(&t); + return err; +} + +#endif diff --git a/src/libtommath/mp_pack_count.c b/src/libtommath/mp_pack_count.c new file mode 100644 index 000000000..aa682ba6c --- /dev/null +++ b/src/libtommath/mp_pack_count.c @@ -0,0 +1,12 @@ +#include "tommath_private.h" +#ifdef MP_PACK_COUNT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +size_t mp_pack_count(const mp_int *a, size_t nails, size_t size) +{ + size_t bits = (size_t)mp_count_bits(a); + return ((bits / ((size * 8u) - nails)) + (((bits % ((size * 8u) - nails)) != 0u) ? 1u : 0u)); +} + +#endif diff --git a/src/libtommath/mp_prime_fermat.c b/src/libtommath/mp_prime_fermat.c new file mode 100644 index 000000000..ac8116fef --- /dev/null +++ b/src/libtommath/mp_prime_fermat.c @@ -0,0 +1,41 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_FERMAT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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. + */ +mp_err mp_prime_fermat(const mp_int *a, const mp_int *b, bool *result) +{ + mp_int t; + mp_err err; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1uL) != 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 LBL_ERR; + } + + /* is it equal to b? */ + *result = mp_cmp(&t, b) == MP_EQ; + +LBL_ERR: + mp_clear(&t); + return err; +} +#endif diff --git a/src/libtommath/mp_prime_frobenius_underwood.c b/src/libtommath/mp_prime_frobenius_underwood.c new file mode 100644 index 000000000..62d3476a9 --- /dev/null +++ b/src/libtommath/mp_prime_frobenius_underwood.c @@ -0,0 +1,127 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_FROBENIUS_UNDERWOOD_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* + * See file mp_prime_is_prime.c or the documentation in doc/bn.tex for the details + */ +#ifndef LTM_USE_ONLY_MR + +/* + * floor of positive solution of + * (2^16)-1 = (a+4)*(2*a+5) + * TODO: Both values are smaller than N^(1/4), would have to use a bigint + * for a instead but any a biger than about 120 are already so rare that + * it is possible to ignore them and still get enough pseudoprimes. + * But it is still a restriction of the set of available pseudoprimes + * which makes this implementation less secure if used stand-alone. + */ +#define LTM_FROBENIUS_UNDERWOOD_A 32764 + +mp_err mp_prime_frobenius_underwood(const mp_int *N, bool *result) +{ + mp_int T1z, T2z, Np1z, sz, tz; + int a, ap2, i; + mp_err err; + + if ((err = mp_init_multi(&T1z, &T2z, &Np1z, &sz, &tz, NULL)) != MP_OKAY) { + return err; + } + + for (a = 0; a < LTM_FROBENIUS_UNDERWOOD_A; a++) { + int j; + + /* TODO: That's ugly! No, really, it is! */ + if ((a==2) || (a==4) || (a==7) || (a==8) || (a==10) || + (a==14) || (a==18) || (a==23) || (a==26) || (a==28)) { + continue; + } + + mp_set_i32(&T1z, (int32_t)((a * a) - 4)); + + if ((err = mp_kronecker(&T1z, N, &j)) != MP_OKAY) goto LBL_END; + + if (j == -1) { + break; + } + + if (j == 0) { + /* composite */ + *result = false; + goto LBL_END; + } + } + /* Tell it a composite and set return value accordingly */ + if (a >= LTM_FROBENIUS_UNDERWOOD_A) { + err = MP_ITER; + goto LBL_END; + } + /* Composite if N and (a+4)*(2*a+5) are not coprime */ + mp_set_u32(&T1z, (uint32_t)((a+4)*((2*a)+5))); + + if ((err = mp_gcd(N, &T1z, &T1z)) != MP_OKAY) goto LBL_END; + + if (!((T1z.used == 1) && (T1z.dp[0] == 1u))) { + /* composite */ + *result = false; + goto LBL_END; + } + + ap2 = a + 2; + if ((err = mp_add_d(N, 1uL, &Np1z)) != MP_OKAY) goto LBL_END; + + mp_set(&sz, 1uL); + mp_set(&tz, 2uL); + + for (i = mp_count_bits(&Np1z) - 2; i >= 0; i--) { + /* + * temp = (sz*(a*sz+2*tz))%N; + * tz = ((tz-sz)*(tz+sz))%N; + * sz = temp; + */ + if ((err = mp_mul_2(&tz, &T2z)) != MP_OKAY) goto LBL_END; + + /* a = 0 at about 50% of the cases (non-square and odd input) */ + if (a != 0) { + if ((err = mp_mul_d(&sz, (mp_digit)a, &T1z)) != MP_OKAY) goto LBL_END; + if ((err = mp_add(&T1z, &T2z, &T2z)) != MP_OKAY) goto LBL_END; + } + + if ((err = mp_mul(&T2z, &sz, &T1z)) != MP_OKAY) goto LBL_END; + if ((err = mp_sub(&tz, &sz, &T2z)) != MP_OKAY) goto LBL_END; + if ((err = mp_add(&sz, &tz, &sz)) != MP_OKAY) goto LBL_END; + if ((err = mp_mul(&sz, &T2z, &tz)) != MP_OKAY) goto LBL_END; + if ((err = mp_mod(&tz, N, &tz)) != MP_OKAY) goto LBL_END; + if ((err = mp_mod(&T1z, N, &sz)) != MP_OKAY) goto LBL_END; + if (s_mp_get_bit(&Np1z, i)) { + /* + * temp = (a+2) * sz + tz + * tz = 2 * tz - sz + * sz = temp + */ + if (a == 0) { + if ((err = mp_mul_2(&sz, &T1z)) != MP_OKAY) goto LBL_END; + } else { + if ((err = mp_mul_d(&sz, (mp_digit)ap2, &T1z)) != MP_OKAY) goto LBL_END; + } + if ((err = mp_add(&T1z, &tz, &T1z)) != MP_OKAY) goto LBL_END; + if ((err = mp_mul_2(&tz, &T2z)) != MP_OKAY) goto LBL_END; + if ((err = mp_sub(&T2z, &sz, &tz)) != MP_OKAY) goto LBL_END; + mp_exch(&sz, &T1z); + } + } + + mp_set_u32(&T1z, (uint32_t)((2 * a) + 5)); + if ((err = mp_mod(&T1z, N, &T1z)) != MP_OKAY) goto LBL_END; + + *result = mp_iszero(&sz) && (mp_cmp(&tz, &T1z) == MP_EQ); + +LBL_END: + mp_clear_multi(&tz, &sz, &Np1z, &T2z, &T1z, NULL); + return err; +} + +#endif +#endif diff --git a/src/libtommath/mp_prime_is_prime.c b/src/libtommath/mp_prime_is_prime.c new file mode 100644 index 000000000..7d73864c7 --- /dev/null +++ b/src/libtommath/mp_prime_is_prime.c @@ -0,0 +1,282 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_IS_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* portable integer log of two with small footprint */ +static unsigned int s_floor_ilog2(int value) +{ + unsigned int r = 0; + while ((value >>= 1) != 0) { + r++; + } + return r; +} + +mp_err mp_prime_is_prime(const mp_int *a, int t, bool *result) +{ + mp_int b; + int ix; + bool res; + mp_err err; + + /* default to no */ + *result = false; + + /* Some shortcuts */ + /* N > 3 */ + if (a->used == 1) { + if ((a->dp[0] == 0u) || (a->dp[0] == 1u)) { + *result = false; + return MP_OKAY; + } + if (a->dp[0] == 2u) { + *result = true; + return MP_OKAY; + } + } + + /* N must be odd */ + if (mp_iseven(a)) { + return MP_OKAY; + } + /* N is not a perfect square: floor(sqrt(N))^2 != N */ + if ((err = mp_is_square(a, &res)) != MP_OKAY) { + return err; + } + if (res) { + return MP_OKAY; + } + + /* is the input equal to one of the primes in the table? */ + for (ix = 0; ix < MP_PRIME_TAB_SIZE; ix++) { + if (mp_cmp_d(a, s_mp_prime_tab[ix]) == MP_EQ) { + *result = true; + return MP_OKAY; + } + } + /* first perform trial division */ + if ((err = s_mp_prime_is_divisible(a, &res)) != MP_OKAY) { + return err; + } + + /* return if it was trivially divisible */ + if (res) { + return MP_OKAY; + } + + /* + Run the Miller-Rabin test with base 2 for the BPSW test. + */ + if ((err = mp_init_set(&b, 2uL)) != MP_OKAY) { + return err; + } + + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + if (!res) { + goto LBL_B; + } + /* + Rumours have it that Mathematica does a second M-R test with base 3. + Other rumours have it that their strong L-S test is slightly different. + It does not hurt, though, beside a bit of extra runtime. + */ + b.dp[0]++; + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + if (!res) { + goto LBL_B; + } + + /* + * Both, the Frobenius-Underwood test and the the Lucas-Selfridge test are quite + * slow so if speed is an issue, define LTM_USE_ONLY_MR to use M-R tests with + * bases 2, 3 and t random bases. + */ +#ifndef LTM_USE_ONLY_MR + if (t >= 0) { +#ifdef LTM_USE_FROBENIUS_TEST + err = mp_prime_frobenius_underwood(a, &res); + if ((err != MP_OKAY) && (err != MP_ITER)) { + goto LBL_B; + } + if (!res) { + goto LBL_B; + } +#else + if ((err = mp_prime_strong_lucas_selfridge(a, &res)) != MP_OKAY) { + goto LBL_B; + } + if (!res) { + goto LBL_B; + } +#endif + } +#endif + + /* run at least one Miller-Rabin test with a random base */ + if (t == 0) { + t = 1; + } + + /* + Only recommended if the input range is known to be < 3317044064679887385961981 + + It uses the bases necessary for a deterministic M-R test if the input is + smaller than 3317044064679887385961981 + The caller has to check the size. + TODO: can be made a bit finer grained but comparing is not free. + */ + if (t < 0) { + int p_max = 0; + + /* + Sorenson, Jonathan; Webster, Jonathan (2015). + "Strong Pseudoprimes to Twelve Prime Bases". + */ + /* 0x437ae92817f9fc85b7e5 = 318665857834031151167461 */ + if ((err = mp_read_radix(&b, "437ae92817f9fc85b7e5", 16)) != MP_OKAY) { + goto LBL_B; + } + + if (mp_cmp(a, &b) == MP_LT) { + p_max = 12; + } else { + /* 0x2be6951adc5b22410a5fd = 3317044064679887385961981 */ + if ((err = mp_read_radix(&b, "2be6951adc5b22410a5fd", 16)) != MP_OKAY) { + goto LBL_B; + } + + if (mp_cmp(a, &b) == MP_LT) { + p_max = 13; + } else { + err = MP_VAL; + goto LBL_B; + } + } + + /* we did bases 2 and 3 already, skip them */ + for (ix = 2; ix < p_max; ix++) { + mp_set(&b, s_mp_prime_tab[ix]); + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + if (!res) { + goto LBL_B; + } + } + } + /* + Do "t" M-R tests with random bases between 3 and "a". + See Fips 186.4 p. 126ff + */ + else if (t > 0) { + unsigned int mask; + int size_a; + + /* + * The mp_digit's have a defined bit-size but the size of the + * array a.dp is a simple 'int' and this library can not assume full + * compliance to the current C-standard (ISO/IEC 9899:2011) because + * it gets used for small embeded processors, too. Some of those MCUs + * have compilers that one cannot call standard compliant by any means. + * Hence the ugly type-fiddling in the following code. + */ + size_a = mp_count_bits(a); + mask = (1u << s_floor_ilog2(size_a)) - 1u; + /* + Assuming the General Rieman hypothesis (never thought to write that in a + comment) the upper bound can be lowered to 2*(log a)^2. + E. Bach, "Explicit bounds for primality testing and related problems," + Math. Comp. 55 (1990), 355-380. + + size_a = (size_a/10) * 7; + len = 2 * (size_a * size_a); + + E.g.: a number of size 2^2048 would be reduced to the upper limit + + floor(2048/10)*7 = 1428 + 2 * 1428^2 = 4078368 + + (would have been ~4030331.9962 with floats and natural log instead) + That number is smaller than 2^28, the default bit-size of mp_digit. + */ + + /* + How many tests, you might ask? Dana Jacobsen of Math::Prime::Util fame + does exactly 1. In words: one. Look at the end of _GMP_is_prime() in + Math-Prime-Util-GMP-0.50/primality.c if you do not believe it. + + The function mp_rand() goes to some length to use a cryptographically + good PRNG. That also means that the chance to always get the same base + in the loop is non-zero, although very low. + If the BPSW test and/or the addtional Frobenious test have been + performed instead of just the Miller-Rabin test with the bases 2 and 3, + a single extra test should suffice, so such a very unlikely event + will not do much harm. + + To preemptivly answer the dangling question: no, a witness does not + need to be prime. + */ + for (ix = 0; ix < t; ix++) { + unsigned int fips_rand; + int len; + + /* mp_rand() guarantees the first digit to be non-zero */ + if ((err = mp_rand(&b, 1)) != MP_OKAY) { + goto LBL_B; + } + /* + * Reduce digit before casting because mp_digit might be bigger than + * an unsigned int and "mask" on the other side is most probably not. + */ + fips_rand = (unsigned int)(b.dp[0] & (mp_digit) mask); + if (fips_rand > (unsigned int)(INT_MAX - MP_DIGIT_BIT)) { + len = INT_MAX / MP_DIGIT_BIT; + } else { + len = (((int)fips_rand + MP_DIGIT_BIT) / MP_DIGIT_BIT); + } + /* Unlikely. */ + if (len < 0) { + ix--; + continue; + } + if ((err = mp_rand(&b, len)) != MP_OKAY) { + goto LBL_B; + } + /* + * That number might got too big and the witness has to be + * smaller than "a" + */ + len = mp_count_bits(&b); + if (len >= size_a) { + len = (len - size_a) + 1; + if ((err = mp_div_2d(&b, len, &b, NULL)) != MP_OKAY) { + goto LBL_B; + } + } + /* Although the chance for b <= 3 is miniscule, try again. */ + if (mp_cmp_d(&b, 3uL) != MP_GT) { + ix--; + continue; + } + if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { + goto LBL_B; + } + if (!res) { + goto LBL_B; + } + } + } + + /* passed the test */ + *result = true; +LBL_B: + mp_clear(&b); + return err; +} + +#endif diff --git a/src/libtommath/mp_prime_miller_rabin.c b/src/libtommath/mp_prime_miller_rabin.c new file mode 100644 index 000000000..4c23a9f28 --- /dev/null +++ b/src/libtommath/mp_prime_miller_rabin.c @@ -0,0 +1,91 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_MILLER_RABIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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. + */ +mp_err mp_prime_miller_rabin(const mp_int *a, const mp_int *b, bool *result) +{ + mp_int n1, y, r; + mp_err err; + int s, j; + + /* ensure b > 1 */ + if (mp_cmp_d(b, 1uL) != 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, 1uL, &n1)) != MP_OKAY) { + goto LBL_ERR1; + } + + /* set 2**s * r = n1 */ + if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) { + goto LBL_ERR1; + } + + /* 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 LBL_ERR2; + } + + /* compute y = b**r mod a */ + if ((err = mp_init(&y)) != MP_OKAY) { + goto LBL_ERR2; + } + if ((err = mp_exptmod(b, &r, a, &y)) != MP_OKAY) { + goto LBL_END; + } + + /* if y != 1 and y != n1 do */ + if ((mp_cmp_d(&y, 1uL) != 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 LBL_END; + } + + /* if y == 1 then composite */ + if (mp_cmp_d(&y, 1uL) == MP_EQ) { + *result = false; + goto LBL_END; + } + + ++j; + } + + /* if y != n1 then composite */ + if (mp_cmp(&y, &n1) != MP_EQ) { + *result = false; + goto LBL_END; + } + } + + /* probably prime now */ + *result = true; + +LBL_END: + mp_clear(&y); +LBL_ERR2: + mp_clear(&r); +LBL_ERR1: + mp_clear(&n1); + return err; +} +#endif diff --git a/src/libtommath/mp_prime_next_prime.c b/src/libtommath/mp_prime_next_prime.c new file mode 100644 index 000000000..6faa08de7 --- /dev/null +++ b/src/libtommath/mp_prime_next_prime.c @@ -0,0 +1,127 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_NEXT_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = true means the prime must be congruent to 3 mod 4 + */ +mp_err mp_prime_next_prime(mp_int *a, int t, bool bbs_style) +{ + int x; + mp_err err; + bool res = false; + mp_digit res_tab[MP_PRIME_TAB_SIZE], kstep; + mp_int b; + + /* force positive */ + a->sign = MP_ZPOS; + + /* simple algo if a is less than the largest prime in the table */ + if (mp_cmp_d(a, s_mp_prime_tab[MP_PRIME_TAB_SIZE-1]) == MP_LT) { + /* find which prime it is bigger than "a" */ + for (x = 0; x < MP_PRIME_TAB_SIZE; x++) { + mp_ord cmp = mp_cmp_d(a, s_mp_prime_tab[x]); + if (cmp == MP_EQ) { + continue; + } + if (cmp != MP_GT) { + if ((bbs_style) && ((s_mp_prime_tab[x] & 3u) != 3u)) { + /* try again until we get a prime congruent to 3 mod 4 */ + continue; + } else { + mp_set(a, s_mp_prime_tab[x]); + return MP_OKAY; + } + } + } + /* fall through to the sieve */ + } + + /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ + kstep = bbs_style ? 4 : 2; + + /* at this point we will use a combination of a sieve and Miller-Rabin */ + + if (bbs_style) { + /* if a mod 4 != 3 subtract the correct value to make it so */ + if ((a->dp[0] & 3u) != 3u) { + if ((err = mp_sub_d(a, (a->dp[0] & 3u) + 1u, a)) != MP_OKAY) { + return err; + } + } + } else { + if (mp_iseven(a)) { + /* force odd */ + if ((err = mp_sub_d(a, 1uL, a)) != MP_OKAY) { + return err; + } + } + } + + /* generate the restable */ + for (x = 1; x < MP_PRIME_TAB_SIZE; x++) { + if ((err = mp_mod_d(a, s_mp_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 (;;) { + mp_digit step = 0; + bool y; + /* skip to the next non-trivially divisible candidate */ + do { + /* y == true if any residue was zero [e.g. cannot be prime] */ + y = false; + + /* increase step to next candidate */ + step += kstep; + + /* compute the new residue without using division */ + for (x = 1; x < MP_PRIME_TAB_SIZE; x++) { + /* add the step to each residue */ + res_tab[x] += kstep; + + /* subtract the modulus [instead of using division] */ + if (res_tab[x] >= s_mp_prime_tab[x]) { + res_tab[x] -= s_mp_prime_tab[x]; + } + + /* set flag if zero */ + if (res_tab[x] == 0u) { + y = true; + } + } + } while (y && (step < (((mp_digit)1 << MP_DIGIT_BIT) - kstep))); + + /* add the step */ + if ((err = mp_add_d(a, step, a)) != MP_OKAY) { + goto LBL_ERR; + } + + /* if didn't pass sieve and step == MP_MAX then skip test */ + if (y && (step >= (((mp_digit)1 << MP_DIGIT_BIT) - kstep))) { + continue; + } + + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { + goto LBL_ERR; + } + if (res) { + break; + } + } + +LBL_ERR: + mp_clear(&b); + return err; +} + +#endif diff --git a/src/libtommath/mp_prime_rabin_miller_trials.c b/src/libtommath/mp_prime_rabin_miller_trials.c new file mode 100644 index 000000000..9f66f8d77 --- /dev/null +++ b/src/libtommath/mp_prime_rabin_miller_trials.c @@ -0,0 +1,48 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_RABIN_MILLER_TRIALS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +static const struct { + int k, t; +} sizes[] = { + { 80, -1 }, /* Use deterministic algorithm for size <= 80 bits */ + { 81, 37 }, /* max. error = 2^(-96)*/ + { 96, 32 }, /* max. error = 2^(-96)*/ + { 128, 40 }, /* max. error = 2^(-112)*/ + { 160, 35 }, /* max. error = 2^(-112)*/ + { 256, 27 }, /* max. error = 2^(-128)*/ + { 384, 16 }, /* max. error = 2^(-128)*/ + { 512, 18 }, /* max. error = 2^(-160)*/ + { 768, 11 }, /* max. error = 2^(-160)*/ + { 896, 10 }, /* max. error = 2^(-160)*/ + { 1024, 12 }, /* max. error = 2^(-192)*/ + { 1536, 8 }, /* max. error = 2^(-192)*/ + { 2048, 6 }, /* max. error = 2^(-192)*/ + { 3072, 4 }, /* max. error = 2^(-192)*/ + { 4096, 5 }, /* max. error = 2^(-256)*/ + { 5120, 4 }, /* max. error = 2^(-256)*/ + { 6144, 4 }, /* max. error = 2^(-256)*/ + { 8192, 3 }, /* max. error = 2^(-256)*/ + { 9216, 3 }, /* max. error = 2^(-256)*/ + { 10240, 2 } /* For bigger keysizes use always at least 2 Rounds */ +}; + +/* 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; + } + if (sizes[x].k > size) { + return (x == 0) ? sizes[0].t : sizes[x - 1].t; + } + } + return sizes[x-1].t; +} + + +#endif diff --git a/src/libtommath/mp_prime_rand.c b/src/libtommath/mp_prime_rand.c new file mode 100644 index 000000000..c5cebbda7 --- /dev/null +++ b/src/libtommath/mp_prime_rand.c @@ -0,0 +1,123 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_RAND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * MP_PRIME_BBS - make prime congruent to 3 mod 4 + * MP_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies MP_PRIME_BBS) + * MP_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! */ +mp_err mp_prime_rand(mp_int *a, int t, int size, int flags) +{ + uint8_t *tmp, maskAND, maskOR_msb, maskOR_lsb; + int bsize, maskOR_msb_offset; + bool res; + mp_err err; + + /* sanity check the input */ + if ((size <= 1) || (t <= 0)) { + return MP_VAL; + } + + /* MP_PRIME_SAFE implies MP_PRIME_BBS */ + if ((flags & MP_PRIME_SAFE) != 0) { + flags |= MP_PRIME_BBS; + } + + /* calc the byte size */ + bsize = (size>>3) + ((size&7)?1:0); + + /* we need a buffer of bsize bytes */ + tmp = (uint8_t *) MP_MALLOC((size_t)bsize); + if (tmp == NULL) { + return MP_MEM; + } + + /* calc the maskAND value for the MSbyte*/ + maskAND = ((size&7) == 0) ? 0xFFu : (uint8_t)(0xFFu >> (8 - (size & 7))); + + /* calc the maskOR_msb */ + maskOR_msb = 0; + maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; + if ((flags & MP_PRIME_2MSB_ON) != 0) { + maskOR_msb |= (uint8_t)(0x80 >> ((9 - size) & 7)); + } + + /* get the maskOR_lsb */ + maskOR_lsb = 1u; + if ((flags & MP_PRIME_BBS) != 0) { + maskOR_lsb |= 3u; + } + + do { + /* read the bytes */ + if ((err = s_mp_rand_source(tmp, (size_t)bsize)) != MP_OKAY) { + goto LBL_ERR; + } + + /* work over the MSbyte */ + tmp[0] &= maskAND; + tmp[0] |= (uint8_t)(1 << ((size - 1) & 7)); + + /* mix in the maskORs */ + tmp[maskOR_msb_offset] |= maskOR_msb; + tmp[bsize-1] |= maskOR_lsb; + + /* read it in */ + /* TODO: casting only for now until all lengths have been changed to the type "size_t"*/ + if ((err = mp_from_ubin(a, tmp, (size_t)bsize)) != MP_OKAY) { + goto LBL_ERR; + } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { + goto LBL_ERR; + } + if (!res) { + continue; + } + + if ((flags & MP_PRIME_SAFE) != 0) { + /* see if (a-1)/2 is prime */ + if ((err = mp_sub_d(a, 1uL, a)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_div_2(a, a)) != MP_OKAY) { + goto LBL_ERR; + } + + /* is it prime? */ + if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { + goto LBL_ERR; + } + } + } while (!res); + + if ((flags & MP_PRIME_SAFE) != 0) { + /* restore a to the original value */ + if ((err = mp_mul_2(a, a)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_add_d(a, 1uL, a)) != MP_OKAY) { + goto LBL_ERR; + } + } + + err = MP_OKAY; +LBL_ERR: + MP_FREE_BUF(tmp, (size_t)bsize); + return err; +} + +#endif diff --git a/src/libtommath/mp_prime_strong_lucas_selfridge.c b/src/libtommath/mp_prime_strong_lucas_selfridge.c new file mode 100644 index 000000000..ffbd9d34f --- /dev/null +++ b/src/libtommath/mp_prime_strong_lucas_selfridge.c @@ -0,0 +1,281 @@ +#include "tommath_private.h" +#ifdef MP_PRIME_STRONG_LUCAS_SELFRIDGE_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* + * See file mp_prime_is_prime.c or the documentation in doc/bn.tex for the details + */ +#ifndef LTM_USE_ONLY_MR + +/* + * multiply bigint a with int d and put the result in c + * Like mp_mul_d() but with a signed long as the small input + */ +static mp_err s_mul_si(const mp_int *a, int32_t d, mp_int *c) +{ + mp_int t; + mp_err err; + + if ((err = mp_init(&t)) != MP_OKAY) { + return err; + } + + /* + * mp_digit might be smaller than a long, which excludes + * the use of mp_mul_d() here. + */ + mp_set_i32(&t, d); + err = mp_mul(a, &t, c); + mp_clear(&t); + return err; +} +/* + Strong Lucas-Selfridge test. + returns true if it is a strong L-S prime, false if it is composite + + Code ported from Thomas Ray Nicely's implementation of the BPSW test + at http://www.trnicely.net/misc/bpsw.html + + Freeware copyright (C) 2016 Thomas R. Nicely . + Released into the public domain by the author, who disclaims any legal + liability arising from its use + + The multi-line comments are made by Thomas R. Nicely and are copied verbatim. + Additional comments marked "CZ" (without the quotes) are by the code-portist. + + (If that name sounds familiar, he is the guy who found the fdiv bug in the + Pentium (P5x, I think) Intel processor) +*/ +mp_err mp_prime_strong_lucas_selfridge(const mp_int *a, bool *result) +{ + /* CZ TODO: choose better variable names! */ + mp_int Dz, gcd, Np1, Uz, Vz, U2mz, V2mz, Qmz, Q2mz, Qkdz, T1z, T2z, T3z, T4z, Q2kdz; + int32_t D, Ds, J, sign, P, Q, r, s, u, Nbits; + mp_err err; + bool oddness; + + *result = false; + /* + Find the first element D in the sequence {5, -7, 9, -11, 13, ...} + such that Jacobi(D,N) = -1 (Selfridge's algorithm). Theory + indicates that, if N is not a perfect square, D will "nearly + always" be "small." Just in case, an overflow trap for D is + included. + */ + + if ((err = mp_init_multi(&Dz, &gcd, &Np1, &Uz, &Vz, &U2mz, &V2mz, &Qmz, &Q2mz, &Qkdz, &T1z, &T2z, &T3z, &T4z, &Q2kdz, + NULL)) != MP_OKAY) { + return err; + } + + D = 5; + sign = 1; + + for (;;) { + Ds = sign * D; + sign = -sign; + mp_set_u32(&Dz, (uint32_t)D); + if ((err = mp_gcd(a, &Dz, &gcd)) != MP_OKAY) goto LBL_LS_ERR; + + /* if 1 < GCD < N then N is composite with factor "D", and + Jacobi(D,N) is technically undefined (but often returned + as zero). */ + if ((mp_cmp_d(&gcd, 1uL) == MP_GT) && (mp_cmp(&gcd, a) == MP_LT)) { + goto LBL_LS_ERR; + } + if (Ds < 0) { + Dz.sign = MP_NEG; + } + if ((err = mp_kronecker(&Dz, a, &J)) != MP_OKAY) goto LBL_LS_ERR; + + if (J == -1) { + break; + } + D += 2; + + if (D > (INT_MAX - 2)) { + err = MP_VAL; + goto LBL_LS_ERR; + } + } + + + + P = 1; /* Selfridge's choice */ + Q = (1 - Ds) / 4; /* Required so D = P*P - 4*Q */ + + /* NOTE: The conditions (a) N does not divide Q, and + (b) D is square-free or not a perfect square, are included by + some authors; e.g., "Prime numbers and computer methods for + factorization," Hans Riesel (2nd ed., 1994, Birkhauser, Boston), + p. 130. For this particular application of Lucas sequences, + these conditions were found to be immaterial. */ + + /* Now calculate N - Jacobi(D,N) = N + 1 (even), and calculate the + odd positive integer d and positive integer s for which + N + 1 = 2^s*d (similar to the step for N - 1 in Miller's test). + The strong Lucas-Selfridge test then returns N as a strong + Lucas probable prime (slprp) if any of the following + conditions is met: U_d=0, V_d=0, V_2d=0, V_4d=0, V_8d=0, + V_16d=0, ..., etc., ending with V_{2^(s-1)*d}=V_{(N+1)/2}=0 + (all equalities mod N). Thus d is the highest index of U that + must be computed (since V_2m is independent of U), compared + to U_{N+1} for the standard Lucas-Selfridge test; and no + index of V beyond (N+1)/2 is required, just as in the + standard Lucas-Selfridge test. However, the quantity Q^d must + be computed for use (if necessary) in the latter stages of + the test. The result is that the strong Lucas-Selfridge test + has a running time only slightly greater (order of 10 %) than + that of the standard Lucas-Selfridge test, while producing + only (roughly) 30 % as many pseudoprimes (and every strong + Lucas pseudoprime is also a standard Lucas pseudoprime). Thus + the evidence indicates that the strong Lucas-Selfridge test is + more effective than the standard Lucas-Selfridge test, and a + Baillie-PSW test based on the strong Lucas-Selfridge test + should be more reliable. */ + + if ((err = mp_add_d(a, 1uL, &Np1)) != MP_OKAY) goto LBL_LS_ERR; + s = mp_cnt_lsb(&Np1); + + /* CZ + * This should round towards zero because + * Thomas R. Nicely used GMP's mpz_tdiv_q_2exp() + * and mp_div_2d() is equivalent. Additionally: + * dividing an even number by two does not produce + * any leftovers. + */ + if ((err = mp_div_2d(&Np1, s, &Dz, NULL)) != MP_OKAY) goto LBL_LS_ERR; + /* We must now compute U_d and V_d. Since d is odd, the accumulated + values U and V are initialized to U_1 and V_1 (if the target + index were even, U and V would be initialized instead to U_0=0 + and V_0=2). The values of U_2m and V_2m are also initialized to + U_1 and V_1; the FOR loop calculates in succession U_2 and V_2, + U_4 and V_4, U_8 and V_8, etc. If the corresponding bits + (1, 2, 3, ...) of t are on (the zero bit having been accounted + for in the initialization of U and V), these values are then + combined with the previous totals for U and V, using the + composition formulas for addition of indices. */ + + mp_set(&Uz, 1uL); /* U=U_1 */ + mp_set(&Vz, (mp_digit)P); /* V=V_1 */ + mp_set(&U2mz, 1uL); /* U_1 */ + mp_set(&V2mz, (mp_digit)P); /* V_1 */ + + mp_set_i32(&Qmz, Q); + if ((err = mp_mul_2(&Qmz, &Q2mz)) != MP_OKAY) goto LBL_LS_ERR; + /* Initializes calculation of Q^d */ + mp_set_i32(&Qkdz, Q); + + Nbits = mp_count_bits(&Dz); + + for (u = 1; u < Nbits; u++) { /* zero bit off, already accounted for */ + /* Formulas for doubling of indices (carried out mod N). Note that + * the indices denoted as "2m" are actually powers of 2, specifically + * 2^(ul-1) beginning each loop and 2^ul ending each loop. + * + * U_2m = U_m*V_m + * V_2m = V_m*V_m - 2*Q^m + */ + + if ((err = mp_mul(&U2mz, &V2mz, &U2mz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mod(&U2mz, a, &U2mz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_sqr(&V2mz, &V2mz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_sub(&V2mz, &Q2mz, &V2mz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mod(&V2mz, a, &V2mz)) != MP_OKAY) goto LBL_LS_ERR; + + /* Must calculate powers of Q for use in V_2m, also for Q^d later */ + if ((err = mp_sqr(&Qmz, &Qmz)) != MP_OKAY) goto LBL_LS_ERR; + + /* prevents overflow */ /* CZ still necessary without a fixed prealloc'd mem.? */ + if ((err = mp_mod(&Qmz, a, &Qmz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mul_2(&Qmz, &Q2mz)) != MP_OKAY) goto LBL_LS_ERR; + + if (s_mp_get_bit(&Dz, u)) { + /* Formulas for addition of indices (carried out mod N); + * + * U_(m+n) = (U_m*V_n + U_n*V_m)/2 + * V_(m+n) = (V_m*V_n + D*U_m*U_n)/2 + * + * Be careful with division by 2 (mod N)! + */ + if ((err = mp_mul(&U2mz, &Vz, &T1z)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mul(&Uz, &V2mz, &T2z)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mul(&V2mz, &Vz, &T3z)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mul(&U2mz, &Uz, &T4z)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = s_mul_si(&T4z, Ds, &T4z)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_add(&T1z, &T2z, &Uz)) != MP_OKAY) goto LBL_LS_ERR; + if (mp_isodd(&Uz)) { + if ((err = mp_add(&Uz, a, &Uz)) != MP_OKAY) goto LBL_LS_ERR; + } + /* CZ + * This should round towards negative infinity because + * Thomas R. Nicely used GMP's mpz_fdiv_q_2exp(). + * But mp_div_2() does not do so, it is truncating instead. + */ + oddness = mp_isodd(&Uz); + if ((err = mp_div_2(&Uz, &Uz)) != MP_OKAY) goto LBL_LS_ERR; + if (mp_isneg(&Uz) && oddness) { + if ((err = mp_sub_d(&Uz, 1uL, &Uz)) != MP_OKAY) goto LBL_LS_ERR; + } + if ((err = mp_add(&T3z, &T4z, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + if (mp_isodd(&Vz)) { + if ((err = mp_add(&Vz, a, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + } + oddness = mp_isodd(&Vz); + if ((err = mp_div_2(&Vz, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + if (mp_isneg(&Vz) && oddness) { + if ((err = mp_sub_d(&Vz, 1uL, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + } + if ((err = mp_mod(&Uz, a, &Uz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mod(&Vz, a, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + + /* Calculating Q^d for later use */ + if ((err = mp_mul(&Qkdz, &Qmz, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mod(&Qkdz, a, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR; + } + } + + /* If U_d or V_d is congruent to 0 mod N, then N is a prime or a + strong Lucas pseudoprime. */ + if (mp_iszero(&Uz) || mp_iszero(&Vz)) { + *result = true; + goto LBL_LS_ERR; + } + + /* NOTE: Ribenboim ("The new book of prime number records," 3rd ed., + 1995/6) omits the condition V0 on p.142, but includes it on + p. 130. The condition is NECESSARY; otherwise the test will + return false negatives---e.g., the primes 29 and 2000029 will be + returned as composite. */ + + /* Otherwise, we must compute V_2d, V_4d, V_8d, ..., V_{2^(s-1)*d} + by repeated use of the formula V_2m = V_m*V_m - 2*Q^m. If any of + these are congruent to 0 mod N, then N is a prime or a strong + Lucas pseudoprime. */ + + /* Initialize 2*Q^(d*2^r) for V_2m */ + if ((err = mp_mul_2(&Qkdz, &Q2kdz)) != MP_OKAY) goto LBL_LS_ERR; + + for (r = 1; r < s; r++) { + if ((err = mp_sqr(&Vz, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_sub(&Vz, &Q2kdz, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mod(&Vz, a, &Vz)) != MP_OKAY) goto LBL_LS_ERR; + if (mp_iszero(&Vz)) { + *result = true; + goto LBL_LS_ERR; + } + /* Calculate Q^{d*2^r} for next r (final iteration irrelevant). */ + if (r < (s - 1)) { + if ((err = mp_sqr(&Qkdz, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mod(&Qkdz, a, &Qkdz)) != MP_OKAY) goto LBL_LS_ERR; + if ((err = mp_mul_2(&Qkdz, &Q2kdz)) != MP_OKAY) goto LBL_LS_ERR; + } + } +LBL_LS_ERR: + mp_clear_multi(&Q2kdz, &T4z, &T3z, &T2z, &T1z, &Qkdz, &Q2mz, &Qmz, &V2mz, &U2mz, &Vz, &Uz, &Np1, &gcd, &Dz, NULL); + return err; +} +#endif +#endif diff --git a/src/libtommath/mp_radix_size.c b/src/libtommath/mp_radix_size.c new file mode 100644 index 000000000..ca08438bc --- /dev/null +++ b/src/libtommath/mp_radix_size.c @@ -0,0 +1,34 @@ +#include "tommath_private.h" +#ifdef MP_RADIX_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* returns size of ASCII representation */ +mp_err mp_radix_size(const mp_int *a, int radix, size_t *size) +{ + mp_err err; + mp_int a_; + int b; + + /* make sure the radix is in range */ + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + if (mp_iszero(a)) { + *size = 2; + return MP_OKAY; + } + + a_ = *a; + a_.sign = MP_ZPOS; + if ((err = mp_log_n(&a_, radix, &b)) != MP_OKAY) { + return err; + } + + /* mp_ilogb truncates to zero, hence we need one extra put on top and one for `\0`. */ + *size = (size_t)b + 2U + (mp_isneg(a) ? 1U : 0U); + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_radix_size_overestimate.c b/src/libtommath/mp_radix_size_overestimate.c new file mode 100644 index 000000000..3fe81d79d --- /dev/null +++ b/src/libtommath/mp_radix_size_overestimate.c @@ -0,0 +1,17 @@ +#include "tommath_private.h" +#ifdef MP_RADIX_SIZE_OVERESTIMATE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +mp_err mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size) +{ + if (MP_HAS(S_MP_RADIX_SIZE_OVERESTIMATE)) { + return s_mp_radix_size_overestimate(a, radix, size); + } + if (MP_HAS(MP_RADIX_SIZE)) { + return mp_radix_size(a, radix, size); + } + return MP_ERR; +} + +#endif diff --git a/src/libtommath/mp_rand.c b/src/libtommath/mp_rand.c new file mode 100644 index 000000000..19364755a --- /dev/null +++ b/src/libtommath/mp_rand.c @@ -0,0 +1,39 @@ +#include "tommath_private.h" +#ifdef MP_RAND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +mp_err mp_rand(mp_int *a, int digits) +{ + int i; + mp_err err; + + mp_zero(a); + + if (digits <= 0) { + return MP_OKAY; + } + + if ((err = mp_grow(a, digits)) != MP_OKAY) { + return err; + } + + if ((err = s_mp_rand_source(a->dp, (size_t)digits * sizeof(mp_digit))) != MP_OKAY) { + return err; + } + + /* TODO: We ensure that the highest digit is nonzero. Should this be removed? */ + while ((a->dp[digits - 1] & MP_MASK) == 0u) { + if ((err = s_mp_rand_source(a->dp + digits - 1, sizeof(mp_digit))) != MP_OKAY) { + return err; + } + } + + a->used = digits; + for (i = 0; i < digits; ++i) { + a->dp[i] &= MP_MASK; + } + + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_rand_source.c b/src/libtommath/mp_rand_source.c new file mode 100644 index 000000000..e9e876948 --- /dev/null +++ b/src/libtommath/mp_rand_source.c @@ -0,0 +1,12 @@ +#include "tommath_private.h" +#ifdef MP_RAND_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +mp_err(*s_mp_rand_source)(void *out, size_t size) = s_mp_rand_platform; + +void mp_rand_source(mp_err(*source)(void *out, size_t size)) +{ + s_mp_rand_source = (source == NULL) ? s_mp_rand_platform : source; +} +#endif diff --git a/src/libtommath/mp_read_radix.c b/src/libtommath/mp_read_radix.c new file mode 100644 index 000000000..28e6eb60a --- /dev/null +++ b/src/libtommath/mp_read_radix.c @@ -0,0 +1,69 @@ +#include "tommath_private.h" +#ifdef MP_READ_RADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* read a string [ASCII] in a given radix */ +mp_err mp_read_radix(mp_int *a, const char *str, int radix) +{ + mp_err err; + mp_sign sign = MP_ZPOS; + + /* 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; + sign = MP_NEG; + } + + /* set the integer to the default of zero */ + mp_zero(a); + + /* process each digit of the string */ + while (*str != '\0') { + /* 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] + */ + uint8_t y; + char ch = (radix <= 36) ? (char)MP_TOUPPER((int)*str) : *str; + unsigned pos = (unsigned)(ch - '+'); + if (MP_RADIX_MAP_REVERSE_SIZE <= pos) { + break; + } + y = s_mp_radix_map_reverse[pos]; + + /* 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) { + break; + } + if ((err = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) { + return err; + } + if ((err = mp_add_d(a, y, a)) != MP_OKAY) { + return err; + } + ++str; + } + + /* if an illegal character was found, fail. */ + if ((*str != '\0') && (*str != '\r') && (*str != '\n')) { + return MP_VAL; + } + + /* set the sign only if a != 0 */ + if (!mp_iszero(a)) { + a->sign = sign; + } + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_reduce.c b/src/libtommath/mp_reduce.c new file mode 100644 index 000000000..b6fae55cc --- /dev/null +++ b/src/libtommath/mp_reduce.c @@ -0,0 +1,83 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* reduces x mod m, assumes 0 < x < m**2, mu is + * precomputed via mp_reduce_setup. + * From HAC pp.604 Algorithm 14.42 + */ +mp_err mp_reduce(mp_int *x, const mp_int *m, const mp_int *mu) +{ + mp_int q; + mp_err err; + int um = m->used; + + /* q = x */ + if ((err = mp_init_copy(&q, x)) != MP_OKAY) { + return err; + } + + /* q1 = x / b**(k-1) */ + mp_rshd(&q, um - 1); + + /* according to HAC this optimization is ok */ + if ((mp_digit)um > ((mp_digit)1 << (MP_DIGIT_BIT - 1))) { + if ((err = mp_mul(&q, mu, &q)) != MP_OKAY) { + goto LBL_ERR; + } + } else if (MP_HAS(S_MP_MUL_HIGH)) { + if ((err = s_mp_mul_high(&q, mu, &q, um)) != MP_OKAY) { + goto LBL_ERR; + } + } else if (MP_HAS(S_MP_MUL_HIGH_COMBA)) { + if ((err = s_mp_mul_high_comba(&q, mu, &q, um)) != MP_OKAY) { + goto LBL_ERR; + } + } else { + err = MP_VAL; + goto LBL_ERR; + } + + /* q3 = q2 / b**(k+1) */ + mp_rshd(&q, um + 1); + + /* x = x mod b**(k+1), quick (no division) */ + if ((err = mp_mod_2d(x, MP_DIGIT_BIT * (um + 1), x)) != MP_OKAY) { + goto LBL_ERR; + } + + /* q = q * m mod b**(k+1), quick (no division) */ + if ((err = s_mp_mul(&q, m, &q, um + 1)) != MP_OKAY) { + goto LBL_ERR; + } + + /* x = x - q */ + if ((err = mp_sub(x, &q, x)) != MP_OKAY) { + goto LBL_ERR; + } + + /* If x < 0, add b**(k+1) to it */ + if (mp_cmp_d(x, 0uL) == MP_LT) { + mp_set(&q, 1uL); + if ((err = mp_lshd(&q, um + 1)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_add(x, &q, x)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* Back off if it's too big */ + while (mp_cmp(x, m) != MP_LT) { + if ((err = s_mp_sub(x, m, x)) != MP_OKAY) { + goto LBL_ERR; + } + } + +LBL_ERR: + mp_clear(&q); + + return err; +} +#endif diff --git a/src/libtommath/mp_reduce_2k.c b/src/libtommath/mp_reduce_2k.c new file mode 100644 index 000000000..e635f5b90 --- /dev/null +++ b/src/libtommath/mp_reduce_2k.c @@ -0,0 +1,49 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* reduces a modulo n where n is of the form 2**p - d */ +mp_err mp_reduce_2k(mp_int *a, const mp_int *n, mp_digit d) +{ + mp_int q; + mp_err err; + int p; + + if ((err = mp_init(&q)) != MP_OKAY) { + return err; + } + + p = mp_count_bits(n); + for (;;) { + /* q = a/2**p, a = a mod 2**p */ + if ((err = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto LBL_ERR; + } + + if (d != 1u) { + /* q = q * d */ + if ((err = mp_mul_d(&q, d, &q)) != MP_OKAY) { + goto LBL_ERR; + } + } + + /* a = a + q */ + if ((err = s_mp_add(a, &q, a)) != MP_OKAY) { + goto LBL_ERR; + } + + if (mp_cmp_mag(a, n) == MP_LT) { + break; + } + if ((err = s_mp_sub(a, n, a)) != MP_OKAY) { + goto LBL_ERR; + } + } + +LBL_ERR: + mp_clear(&q); + return err; +} + +#endif diff --git a/src/libtommath/mp_reduce_2k_l.c b/src/libtommath/mp_reduce_2k_l.c new file mode 100644 index 000000000..31d9a1882 --- /dev/null +++ b/src/libtommath/mp_reduce_2k_l.c @@ -0,0 +1,52 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* reduces a modulo n where n is of the form 2**p - d + This differs from reduce_2k since "d" can be larger + than a single digit. +*/ +mp_err mp_reduce_2k_l(mp_int *a, const mp_int *n, const mp_int *d) +{ + mp_int q; + mp_err err; + int p; + + if ((err = mp_init(&q)) != MP_OKAY) { + return err; + } + + p = mp_count_bits(n); + + for (;;) { + /* q = a/2**p, a = a mod 2**p */ + if ((err = mp_div_2d(a, p, &q, a)) != MP_OKAY) { + goto LBL_ERR; + } + + /* q = q * d */ + if ((err = mp_mul(&q, d, &q)) != MP_OKAY) { + goto LBL_ERR; + } + + /* a = a + q */ + if ((err = s_mp_add(a, &q, a)) != MP_OKAY) { + goto LBL_ERR; + } + + if (mp_cmp_mag(a, n) == MP_LT) { + break; + } + if ((err = s_mp_sub(a, n, a)) != MP_OKAY) { + goto LBL_ERR; + } + + } + +LBL_ERR: + mp_clear(&q); + return err; +} + +#endif diff --git a/src/libtommath/mp_reduce_2k_setup.c b/src/libtommath/mp_reduce_2k_setup.c new file mode 100644 index 000000000..51f884134 --- /dev/null +++ b/src/libtommath/mp_reduce_2k_setup.c @@ -0,0 +1,30 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_2K_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* determines the setup value */ +mp_err mp_reduce_2k_setup(const mp_int *a, mp_digit *d) +{ + mp_err err; + mp_int tmp; + + if ((err = mp_init(&tmp)) != MP_OKAY) { + return err; + } + + if ((err = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto LBL_ERR; + } + + if ((err = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { + goto LBL_ERR; + } + + *d = tmp.dp[0]; + +LBL_ERR: + mp_clear(&tmp); + return err; +} +#endif diff --git a/src/libtommath/mp_reduce_2k_setup_l.c b/src/libtommath/mp_reduce_2k_setup_l.c new file mode 100644 index 000000000..b647c9d88 --- /dev/null +++ b/src/libtommath/mp_reduce_2k_setup_l.c @@ -0,0 +1,28 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_2K_SETUP_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* determines the setup value */ +mp_err mp_reduce_2k_setup_l(const mp_int *a, mp_int *d) +{ + mp_err err; + mp_int tmp; + + if ((err = mp_init(&tmp)) != MP_OKAY) { + return err; + } + + if ((err = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { + goto LBL_ERR; + } + + if ((err = s_mp_sub(&tmp, a, d)) != MP_OKAY) { + goto LBL_ERR; + } + +LBL_ERR: + mp_clear(&tmp); + return err; +} +#endif diff --git a/src/libtommath/mp_reduce_is_2k.c b/src/libtommath/mp_reduce_is_2k.c new file mode 100644 index 000000000..9774f96e9 --- /dev/null +++ b/src/libtommath/mp_reduce_is_2k.c @@ -0,0 +1,34 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_IS_2K_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* determines if mp_reduce_2k can be used */ +bool mp_reduce_is_2k(const mp_int *a) +{ + if (mp_iszero(a)) { + return false; + } else if (a->used == 1) { + return true; + } else if (a->used > 1) { + int ix, iy = mp_count_bits(a), iw = 1; + mp_digit iz = 1; + + /* Test every bit from the second digit up, must be 1 */ + for (ix = MP_DIGIT_BIT; ix < iy; ix++) { + if ((a->dp[iw] & iz) == 0u) { + return false; + } + iz <<= 1; + if (iz > MP_DIGIT_MAX) { + ++iw; + iz = 1; + } + } + return true; + } else { + return true; + } +} + +#endif diff --git a/src/libtommath/mp_reduce_is_2k_l.c b/src/libtommath/mp_reduce_is_2k_l.c new file mode 100644 index 000000000..101b4a185 --- /dev/null +++ b/src/libtommath/mp_reduce_is_2k_l.c @@ -0,0 +1,27 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_IS_2K_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* determines if reduce_2k_l can be used */ +bool mp_reduce_is_2k_l(const mp_int *a) +{ + if (mp_iszero(a)) { + return false; + } else if (a->used == 1) { + return true; + } else if (a->used > 1) { + /* if more than half of the digits are -1 we're sold */ + int ix, iy; + for (iy = ix = 0; ix < a->used; ix++) { + if (a->dp[ix] == MP_DIGIT_MAX) { + ++iy; + } + } + return (iy >= (a->used/2)); + } else { + return false; + } +} + +#endif diff --git a/src/libtommath/mp_reduce_setup.c b/src/libtommath/mp_reduce_setup.c new file mode 100644 index 000000000..e12056e1e --- /dev/null +++ b/src/libtommath/mp_reduce_setup.c @@ -0,0 +1,17 @@ +#include "tommath_private.h" +#ifdef MP_REDUCE_SETUP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* pre-calculate the value required for Barrett reduction + * For a given modulus "b" it calulates the value required in "a" + */ +mp_err mp_reduce_setup(mp_int *a, const mp_int *b) +{ + mp_err err; + if ((err = mp_2expt(a, b->used * 2 * MP_DIGIT_BIT)) != MP_OKAY) { + return err; + } + return mp_div(a, b, a, NULL); +} +#endif diff --git a/src/libtommath/mp_root_n.c b/src/libtommath/mp_root_n.c new file mode 100644 index 000000000..d904df883 --- /dev/null +++ b/src/libtommath/mp_root_n.c @@ -0,0 +1,141 @@ +#include "tommath_private.h" +#ifdef MP_ROOT_N_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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. + */ +mp_err mp_root_n(const mp_int *a, int b, mp_int *c) +{ + mp_int t1, t2, t3, a_; + int ilog2; + mp_err err; + + if (b < 0 || (unsigned)b > (unsigned)MP_DIGIT_MAX) { + return MP_VAL; + } + + /* input must be positive if b is even */ + if (((b & 1) == 0) && mp_isneg(a)) { + return MP_VAL; + } + + if ((err = mp_init_multi(&t1, &t2, &t3, NULL)) != MP_OKAY) { + return err; + } + + /* if a is negative fudge the sign but keep track */ + a_ = *a; + a_.sign = MP_ZPOS; + + /* Compute seed: 2^(log_2(n)/b + 2)*/ + ilog2 = mp_count_bits(a); + + /* + If "b" is larger than INT_MAX it is also larger than + log_2(n) because the bit-length of the "n" is measured + with an int and hence the root is always < 2 (two). + */ + if (b > INT_MAX/2) { + mp_set(c, 1uL); + c->sign = a->sign; + err = MP_OKAY; + goto LBL_ERR; + } + + /* "b" is smaller than INT_MAX, we can cast safely */ + if (ilog2 < b) { + mp_set(c, 1uL); + c->sign = a->sign; + err = MP_OKAY; + goto LBL_ERR; + } + ilog2 = ilog2 / b; + if (ilog2 == 0) { + mp_set(c, 1uL); + c->sign = a->sign; + err = MP_OKAY; + goto LBL_ERR; + } + /* Start value must be larger than root */ + ilog2 += 2; + if ((err = mp_2expt(&t2,ilog2)) != MP_OKAY) goto LBL_ERR; + do { + /* t1 = t2 */ + if ((err = mp_copy(&t2, &t1)) != MP_OKAY) goto LBL_ERR; + + /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ + + /* t3 = t1**(b-1) */ + if ((err = mp_expt_n(&t1, b - 1, &t3)) != MP_OKAY) goto LBL_ERR; + + /* numerator */ + /* t2 = t1**b */ + if ((err = mp_mul(&t3, &t1, &t2)) != MP_OKAY) goto LBL_ERR; + + /* t2 = t1**b - a */ + if ((err = mp_sub(&t2, &a_, &t2)) != MP_OKAY) goto LBL_ERR; + + /* denominator */ + /* t3 = t1**(b-1) * b */ + if ((err = mp_mul_d(&t3, (mp_digit)b, &t3)) != MP_OKAY) goto LBL_ERR; + + /* t3 = (t1**b - a)/(b * t1**(b-1)) */ + if ((err = mp_div(&t2, &t3, &t3, NULL)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_sub(&t1, &t3, &t2)) != MP_OKAY) goto LBL_ERR; + + /* + Number of rounds is at most log_2(root). If it is more it + got stuck, so break out of the loop and do the rest manually. + */ + if (ilog2-- == 0) { + break; + } + } while (mp_cmp(&t1, &t2) != MP_EQ); + + /* result can be off by a few so check */ + /* Loop beneath can overshoot by one if found root is smaller than actual root */ + for (;;) { + mp_ord cmp; + if ((err = mp_expt_n(&t1, b, &t2)) != MP_OKAY) goto LBL_ERR; + cmp = mp_cmp(&t2, &a_); + if (cmp == MP_EQ) { + err = MP_OKAY; + goto LBL_ERR; + } + if (cmp == MP_LT) { + if ((err = mp_add_d(&t1, 1uL, &t1)) != MP_OKAY) goto LBL_ERR; + } else { + break; + } + } + /* correct overshoot from above or from recurrence */ + for (;;) { + if ((err = mp_expt_n(&t1, b, &t2)) != MP_OKAY) goto LBL_ERR; + if (mp_cmp(&t2, &a_) == MP_GT) { + if ((err = mp_sub_d(&t1, 1uL, &t1)) != MP_OKAY) goto LBL_ERR; + } else { + break; + } + } + + /* set the result */ + mp_exch(&t1, c); + + /* set the sign of the result */ + c->sign = a->sign; + +LBL_ERR: + mp_clear_multi(&t1, &t2, &t3, NULL); + return err; +} + +#endif diff --git a/src/libtommath/mp_rshd.c b/src/libtommath/mp_rshd.c new file mode 100644 index 000000000..3f0a9416e --- /dev/null +++ b/src/libtommath/mp_rshd.c @@ -0,0 +1,43 @@ +#include "tommath_private.h" +#ifdef MP_RSHD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* shift right a certain amount of digits */ +void mp_rshd(mp_int *a, int b) +{ + int x; + + /* if b <= 0 then ignore it */ + if (b <= 0) { + return; + } + + /* if b > used then simply zero it and return */ + if (a->used <= b) { + mp_zero(a); + return; + } + + /* shift the digits down. + * 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++) { + a->dp[x] = a->dp[x + b]; + } + + /* zero the top digits */ + s_mp_zero_digs(a->dp + a->used - b, b); + + /* remove excess digits */ + a->used -= b; +} +#endif diff --git a/src/libtommath/mp_sbin_size.c b/src/libtommath/mp_sbin_size.c new file mode 100644 index 000000000..2f40df3e1 --- /dev/null +++ b/src/libtommath/mp_sbin_size.c @@ -0,0 +1,11 @@ +#include "tommath_private.h" +#ifdef MP_SBIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* get the size for an signed equivalent */ +size_t mp_sbin_size(const mp_int *a) +{ + return 1u + mp_ubin_size(a); +} +#endif diff --git a/src/libtommath/mp_set.c b/src/libtommath/mp_set.c new file mode 100644 index 000000000..bc0c4da76 --- /dev/null +++ b/src/libtommath/mp_set.c @@ -0,0 +1,15 @@ +#include "tommath_private.h" +#ifdef MP_SET_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* set to a digit */ +void mp_set(mp_int *a, mp_digit b) +{ + int oldused = a->used; + a->dp[0] = b & MP_MASK; + a->sign = MP_ZPOS; + a->used = (a->dp[0] != 0u) ? 1 : 0; + s_mp_zero_digs(a->dp + a->used, oldused - a->used); +} +#endif diff --git a/src/libtommath/mp_set_double.c b/src/libtommath/mp_set_double.c new file mode 100644 index 000000000..0ede359c6 --- /dev/null +++ b/src/libtommath/mp_set_double.c @@ -0,0 +1,47 @@ +#include "tommath_private.h" +#ifdef MP_SET_DOUBLE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#if defined(MP_HAS_SET_DOUBLE) +mp_err mp_set_double(mp_int *a, double b) +{ + uint64_t frac; + int exp; + mp_err err; + union { + double dbl; + uint64_t bits; + } cast; + cast.dbl = b; + + exp = (int)((unsigned)(cast.bits >> 52) & 0x7FFu); + frac = (cast.bits & (((uint64_t)1 << 52) - (uint64_t)1)) | ((uint64_t)1 << 52); + + if (exp == 0x7FF) { /* +-inf, NaN */ + return MP_VAL; + } + exp -= 1023 + 52; + + mp_set_u64(a, frac); + + err = (exp < 0) ? mp_div_2d(a, -exp, a, NULL) : mp_mul_2d(a, exp, a); + if (err != MP_OKAY) { + return err; + } + + if (((cast.bits >> 63) != 0u) && !mp_iszero(a)) { + a->sign = MP_NEG; + } + + return MP_OKAY; +} +#else +/* pragma message() not supported by several compilers (in mostly older but still used versions) */ +# ifdef _MSC_VER +# pragma message("mp_set_double implementation is only available on platforms with IEEE754 floating point format") +# else +# warning "mp_set_double implementation is only available on platforms with IEEE754 floating point format" +# endif +#endif +#endif diff --git a/src/libtommath/mp_set_i32.c b/src/libtommath/mp_set_i32.c new file mode 100644 index 000000000..123613ecc --- /dev/null +++ b/src/libtommath/mp_set_i32.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_SET_I32_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_SET_SIGNED(mp_set_i32, mp_set_u32, int32_t, uint32_t) +#endif diff --git a/src/libtommath/mp_set_i64.c b/src/libtommath/mp_set_i64.c new file mode 100644 index 000000000..4635ca3a5 --- /dev/null +++ b/src/libtommath/mp_set_i64.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_SET_I64_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_SET_SIGNED(mp_set_i64, mp_set_u64, int64_t, uint64_t) +#endif diff --git a/src/libtommath/mp_set_l.c b/src/libtommath/mp_set_l.c new file mode 100644 index 000000000..411f6eb08 --- /dev/null +++ b/src/libtommath/mp_set_l.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_SET_L_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_SET_SIGNED(mp_set_l, mp_set_ul, long, unsigned long) +#endif diff --git a/src/libtommath/mp_set_u32.c b/src/libtommath/mp_set_u32.c new file mode 100644 index 000000000..8ff84dc22 --- /dev/null +++ b/src/libtommath/mp_set_u32.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_SET_U32_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_SET_UNSIGNED(mp_set_u32, uint32_t) +#endif diff --git a/src/libtommath/mp_set_u64.c b/src/libtommath/mp_set_u64.c new file mode 100644 index 000000000..9acb1fefe --- /dev/null +++ b/src/libtommath/mp_set_u64.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_SET_U64_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_SET_UNSIGNED(mp_set_u64, uint64_t) +#endif diff --git a/src/libtommath/mp_set_ul.c b/src/libtommath/mp_set_ul.c new file mode 100644 index 000000000..baf8e186e --- /dev/null +++ b/src/libtommath/mp_set_ul.c @@ -0,0 +1,7 @@ +#include "tommath_private.h" +#ifdef MP_SET_UL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +MP_SET_UNSIGNED(mp_set_ul, unsigned long) +#endif diff --git a/src/libtommath/mp_shrink.c b/src/libtommath/mp_shrink.c new file mode 100644 index 000000000..3d9b1626d --- /dev/null +++ b/src/libtommath/mp_shrink.c @@ -0,0 +1,22 @@ +#include "tommath_private.h" +#ifdef MP_SHRINK_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* shrink a bignum */ +mp_err mp_shrink(mp_int *a) +{ + int alloc = MP_MAX(MP_MIN_DIGIT_COUNT, a->used); + if (a->alloc != alloc) { + mp_digit *dp = (mp_digit *) MP_REALLOC(a->dp, + (size_t)a->alloc * sizeof(mp_digit), + (size_t)alloc * sizeof(mp_digit)); + if (dp == NULL) { + return MP_MEM; + } + a->dp = dp; + a->alloc = alloc; + } + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_signed_rsh.c b/src/libtommath/mp_signed_rsh.c new file mode 100644 index 000000000..3b7e232e5 --- /dev/null +++ b/src/libtommath/mp_signed_rsh.c @@ -0,0 +1,21 @@ +#include "tommath_private.h" +#ifdef MP_SIGNED_RSH_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* shift right by a certain bit count with sign extension */ +mp_err mp_signed_rsh(const mp_int *a, int b, mp_int *c) +{ + mp_err err; + if (!mp_isneg(a)) { + return mp_div_2d(a, b, c, NULL); + } + + if ((err = mp_add_d(a, 1uL, c)) != MP_OKAY) { + return err; + } + + err = mp_div_2d(c, b, c, NULL); + return (err == MP_OKAY) ? mp_sub_d(c, 1uL, c) : err; +} +#endif diff --git a/src/libtommath/mp_sqrmod.c b/src/libtommath/mp_sqrmod.c new file mode 100644 index 000000000..bce2af090 --- /dev/null +++ b/src/libtommath/mp_sqrmod.c @@ -0,0 +1,15 @@ +#include "tommath_private.h" +#ifdef MP_SQRMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* c = a * a (mod b) */ +mp_err mp_sqrmod(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_err err; + if ((err = mp_sqr(a, c)) != MP_OKAY) { + return err; + } + return mp_mod(c, b, c); +} +#endif diff --git a/src/libtommath/mp_sqrt.c b/src/libtommath/mp_sqrt.c new file mode 100644 index 000000000..1a9dca7da --- /dev/null +++ b/src/libtommath/mp_sqrt.c @@ -0,0 +1,67 @@ +#include "tommath_private.h" +#ifdef MP_SQRT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* this function is less generic than mp_n_root, simpler and faster */ +mp_err mp_sqrt(const mp_int *arg, mp_int *ret) +{ + mp_err err; + mp_int t1, t2; + + /* must be positive */ + if (mp_isneg(arg)) { + return MP_VAL; + } + + /* easy out */ + if (mp_iszero(arg)) { + mp_zero(ret); + return MP_OKAY; + } + + if ((err = mp_init_copy(&t1, arg)) != MP_OKAY) { + return err; + } + + if ((err = mp_init(&t2)) != MP_OKAY) { + goto LBL_ERR2; + } + + /* First approx. (not very bad for large arg) */ + mp_rshd(&t1, t1.used/2); + + /* t1 > 0 */ + if ((err = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { + goto LBL_ERR1; + } + if ((err = mp_add(&t1, &t2, &t1)) != MP_OKAY) { + goto LBL_ERR1; + } + if ((err = mp_div_2(&t1, &t1)) != MP_OKAY) { + goto LBL_ERR1; + } + /* And now t1 > sqrt(arg) */ + do { + if ((err = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { + goto LBL_ERR1; + } + if ((err = mp_add(&t1, &t2, &t1)) != MP_OKAY) { + goto LBL_ERR1; + } + if ((err = mp_div_2(&t1, &t1)) != MP_OKAY) { + goto LBL_ERR1; + } + /* t1 >= sqrt(arg) >= t2 at this point */ + } while (mp_cmp_mag(&t1, &t2) == MP_GT); + + mp_exch(&t1, ret); + +LBL_ERR1: + mp_clear(&t2); +LBL_ERR2: + mp_clear(&t1); + return err; +} + +#endif diff --git a/src/libtommath/mp_sqrtmod_prime.c b/src/libtommath/mp_sqrtmod_prime.c new file mode 100644 index 000000000..0fae1d02a --- /dev/null +++ b/src/libtommath/mp_sqrtmod_prime.c @@ -0,0 +1,133 @@ +#include "tommath_private.h" +#ifdef MP_SQRTMOD_PRIME_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* Tonelli-Shanks algorithm + * https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm + * https://gmplib.org/list-archives/gmp-discuss/2013-April/005300.html + * + */ + +mp_err mp_sqrtmod_prime(const mp_int *n, const mp_int *prime, mp_int *ret) +{ + mp_err err; + int legendre; + /* The type is "int" because of the types in the mp_int struct. + Don't forget to change them here when you change them there! */ + int S, M, i; + mp_int t1, C, Q, Z, T, R, two; + + /* first handle the simple cases */ + if (mp_cmp_d(n, 0uL) == MP_EQ) { + mp_zero(ret); + return MP_OKAY; + } + /* "prime" must be odd and > 2 */ + if (mp_iseven(prime) || (mp_cmp_d(prime, 3uL) == MP_LT)) return MP_VAL; + if ((err = mp_kronecker(n, prime, &legendre)) != MP_OKAY) return err; + /* n \not\cong 0 (mod p) and n \cong r^2 (mod p) for some r \in N^+ */ + if (legendre != 1) return MP_VAL; + + if ((err = mp_init_multi(&t1, &C, &Q, &Z, &T, &R, &two, NULL)) != MP_OKAY) { + return err; + } + + /* SPECIAL CASE: if prime mod 4 == 3 + * compute directly: err = n^(prime+1)/4 mod prime + * Handbook of Applied Cryptography algorithm 3.36 + */ + /* x%4 == x&3 for x in N and x>0 */ + if ((prime->dp[0] & 3u) == 3u) { + if ((err = mp_add_d(prime, 1uL, &t1)) != MP_OKAY) goto LBL_END; + if ((err = mp_div_2(&t1, &t1)) != MP_OKAY) goto LBL_END; + if ((err = mp_div_2(&t1, &t1)) != MP_OKAY) goto LBL_END; + if ((err = mp_exptmod(n, &t1, prime, ret)) != MP_OKAY) goto LBL_END; + err = MP_OKAY; + goto LBL_END; + } + + /* NOW: Tonelli-Shanks algorithm */ + + /* factor out powers of 2 from prime-1, defining Q and S as: prime-1 = Q*2^S */ + if ((err = mp_copy(prime, &Q)) != MP_OKAY) goto LBL_END; + if ((err = mp_sub_d(&Q, 1uL, &Q)) != MP_OKAY) goto LBL_END; + /* Q = prime - 1 */ + S = 0; + /* S = 0 */ + while (mp_iseven(&Q)) { + if ((err = mp_div_2(&Q, &Q)) != MP_OKAY) goto LBL_END; + /* Q = Q / 2 */ + S++; + /* S = S + 1 */ + } + + /* find a Z such that the Legendre symbol (Z|prime) == -1 */ + mp_set(&Z, 2uL); + /* Z = 2 */ + for (;;) { + if ((err = mp_kronecker(&Z, prime, &legendre)) != MP_OKAY) goto LBL_END; + /* If "prime" (p) is an odd prime Jacobi(k|p) = 0 for k \cong 0 (mod p) */ + /* but there is at least one non-quadratic residue before k>=p if p is an odd prime. */ + if (legendre == 0) { + err = MP_VAL; + goto LBL_END; + } + if (legendre == -1) break; + if ((err = mp_add_d(&Z, 1uL, &Z)) != MP_OKAY) goto LBL_END; + /* Z = Z + 1 */ + } + + if ((err = mp_exptmod(&Z, &Q, prime, &C)) != MP_OKAY) goto LBL_END; + /* C = Z ^ Q mod prime */ + if ((err = mp_add_d(&Q, 1uL, &t1)) != MP_OKAY) goto LBL_END; + if ((err = mp_div_2(&t1, &t1)) != MP_OKAY) goto LBL_END; + /* t1 = (Q + 1) / 2 */ + if ((err = mp_exptmod(n, &t1, prime, &R)) != MP_OKAY) goto LBL_END; + /* R = n ^ ((Q + 1) / 2) mod prime */ + if ((err = mp_exptmod(n, &Q, prime, &T)) != MP_OKAY) goto LBL_END; + /* T = n ^ Q mod prime */ + M = S; + /* M = S */ + mp_set(&two, 2uL); + + for (;;) { + if ((err = mp_copy(&T, &t1)) != MP_OKAY) goto LBL_END; + i = 0; + for (;;) { + if (mp_cmp_d(&t1, 1uL) == MP_EQ) break; + /* No exponent in the range 0 < i < M found + (M is at least 1 in the first round because "prime" > 2) */ + if (M == i) { + err = MP_VAL; + goto LBL_END; + } + if ((err = mp_exptmod(&t1, &two, prime, &t1)) != MP_OKAY) goto LBL_END; + i++; + } + if (i == 0) { + if ((err = mp_copy(&R, ret)) != MP_OKAY) goto LBL_END; + err = MP_OKAY; + goto LBL_END; + } + mp_set_i32(&t1, M - i - 1); + if ((err = mp_exptmod(&two, &t1, prime, &t1)) != MP_OKAY) goto LBL_END; + /* t1 = 2 ^ (M - i - 1) */ + if ((err = mp_exptmod(&C, &t1, prime, &t1)) != MP_OKAY) goto LBL_END; + /* t1 = C ^ (2 ^ (M - i - 1)) mod prime */ + if ((err = mp_sqrmod(&t1, prime, &C)) != MP_OKAY) goto LBL_END; + /* C = (t1 * t1) mod prime */ + if ((err = mp_mulmod(&R, &t1, prime, &R)) != MP_OKAY) goto LBL_END; + /* R = (R * t1) mod prime */ + if ((err = mp_mulmod(&T, &C, prime, &T)) != MP_OKAY) goto LBL_END; + /* T = (T * C) mod prime */ + M = i; + /* M = i */ + } + +LBL_END: + mp_clear_multi(&t1, &C, &Q, &Z, &T, &R, &two, NULL); + return err; +} + +#endif diff --git a/src/libtommath/mp_sub.c b/src/libtommath/mp_sub.c new file mode 100644 index 000000000..1c95ad544 --- /dev/null +++ b/src/libtommath/mp_sub.c @@ -0,0 +1,36 @@ +#include "tommath_private.h" +#ifdef MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* high level subtraction (handles signs) */ +mp_err mp_sub(const mp_int *a, const mp_int *b, mp_int *c) +{ + if (a->sign != b->sign) { + /* 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 = a->sign; + return s_mp_add(a, b, c); + } + + /* 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) { + /* The second has a larger magnitude */ + /* The result has the *opposite* sign from */ + /* the first number. */ + c->sign = (!mp_isneg(a) ? MP_NEG : MP_ZPOS); + MP_EXCH(const mp_int *, a, b); + } else { + /* The first has a larger or equal magnitude */ + /* Copy the sign from the first */ + c->sign = a->sign; + } + return s_mp_sub(a, b, c); +} + +#endif diff --git a/src/libtommath/mp_sub_d.c b/src/libtommath/mp_sub_d.c new file mode 100644 index 000000000..b2b4d723d --- /dev/null +++ b/src/libtommath/mp_sub_d.c @@ -0,0 +1,78 @@ +#include "tommath_private.h" +#ifdef MP_SUB_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* single digit subtraction */ +mp_err mp_sub_d(const mp_int *a, mp_digit b, mp_int *c) +{ + mp_err err; + int oldused; + + /* fast path for a == c */ + if (a == c) { + if ((c->sign == MP_NEG) && + ((c->dp[0] + b) < MP_DIGIT_MAX)) { + c->dp[0] += b; + return MP_OKAY; + } + if ((c->sign == MP_ZPOS) && + (c->dp[0] > b)) { + c->dp[0] -= b; + return MP_OKAY; + } + } + + /* grow c as required */ + if ((err = mp_grow(c, a->used + 1)) != MP_OKAY) { + return err; + } + + /* if a is negative just do an unsigned + * addition [with fudged signs] + */ + if (a->sign == MP_NEG) { + mp_int a_ = *a; + a_.sign = MP_ZPOS; + err = mp_add_d(&a_, b, c); + c->sign = MP_NEG; + + /* clamp */ + mp_clamp(c); + + return err; + } + + oldused = c->used; + + /* if a <= b simply fix the single digit */ + if (((a->used == 1) && (a->dp[0] <= b)) || mp_iszero(a)) { + c->dp[0] = (a->used == 1) ? b - a->dp[0] : b; + + /* negative/1digit */ + c->sign = MP_NEG; + c->used = 1; + } else { + int i; + mp_digit mu = b; + + /* positive/size */ + c->sign = MP_ZPOS; + c->used = a->used; + + /* subtract digits, mu is carry */ + for (i = 0; i < a->used; i++) { + c->dp[i] = a->dp[i] - mu; + mu = c->dp[i] >> (MP_SIZEOF_BITS(mp_digit) - 1u); + c->dp[i] &= MP_MASK; + } + } + + /* zero excess digits */ + s_mp_zero_digs(c->dp + c->used, oldused - c->used); + + mp_clamp(c); + return MP_OKAY; +} + +#endif diff --git a/src/libtommath/mp_submod.c b/src/libtommath/mp_submod.c new file mode 100644 index 000000000..6e4d4f712 --- /dev/null +++ b/src/libtommath/mp_submod.c @@ -0,0 +1,15 @@ +#include "tommath_private.h" +#ifdef MP_SUBMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* d = a - b (mod c) */ +mp_err mp_submod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d) +{ + mp_err err; + if ((err = mp_sub(a, b, d)) != MP_OKAY) { + return err; + } + return mp_mod(d, c, d); +} +#endif diff --git a/src/libtommath/mp_to_radix.c b/src/libtommath/mp_to_radix.c new file mode 100644 index 000000000..1e5e67110 --- /dev/null +++ b/src/libtommath/mp_to_radix.c @@ -0,0 +1,95 @@ +#include "tommath_private.h" +#ifdef MP_TO_RADIX_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* reverse an array, used for radix code */ +static void s_reverse(char *s, size_t len) +{ + size_t ix = 0, iy = len - 1u; + while (ix < iy) { + MP_EXCH(char, s[ix], s[iy]); + ++ix; + --iy; + } +} + +/* stores a bignum as a ASCII string in a given radix (2..64) + * + * Stores upto "size - 1" chars and always a NULL byte, puts the number of characters + * written, including the '\0', in "written". + */ +mp_err mp_to_radix(const mp_int *a, char *str, size_t maxlen, size_t *written, int radix) +{ + size_t digs; + mp_err err; + mp_int t; + mp_digit d; + char *_s = str; + + /* check range of radix and size*/ + if (maxlen < 2u) { + return MP_BUF; + } + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + /* quick out if its zero */ + if (mp_iszero(a)) { + *str++ = '0'; + *str = '\0'; + if (written != NULL) { + *written = 2u; + } + return MP_OKAY; + } + + if ((err = mp_init_copy(&t, a)) != MP_OKAY) { + return err; + } + + /* if it is negative output a - */ + if (mp_isneg(&t)) { + /* 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 = 0u; + while (!mp_iszero(&t)) { + if (--maxlen < 1u) { + /* no more room */ + err = MP_BUF; + goto LBL_ERR; + } + if ((err = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { + goto LBL_ERR; + } + *str++ = s_mp_radix_map[d]; + ++digs; + } + /* reverse the digits of the string. In this case _s points + * to the first digit [excluding the sign] of the number + */ + s_reverse(_s, digs); + + /* append a NULL so the string is properly terminated */ + *str = '\0'; + digs++; + + if (written != NULL) { + *written = mp_isneg(a) ? (digs + 1u): digs; + } + +LBL_ERR: + mp_clear(&t); + return err; +} + +#endif diff --git a/src/libtommath/mp_to_sbin.c b/src/libtommath/mp_to_sbin.c new file mode 100644 index 000000000..00884c3be --- /dev/null +++ b/src/libtommath/mp_to_sbin.c @@ -0,0 +1,22 @@ +#include "tommath_private.h" +#ifdef MP_TO_SBIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* store in signed [big endian] format */ +mp_err mp_to_sbin(const mp_int *a, uint8_t *buf, size_t maxlen, size_t *written) +{ + mp_err err; + if (maxlen == 0u) { + return MP_BUF; + } + if ((err = mp_to_ubin(a, buf + 1, maxlen - 1u, written)) != MP_OKAY) { + return err; + } + if (written != NULL) { + (*written)++; + } + buf[0] = mp_isneg(a) ? (uint8_t)1 : (uint8_t)0; + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_to_ubin.c b/src/libtommath/mp_to_ubin.c new file mode 100644 index 000000000..e8643cc07 --- /dev/null +++ b/src/libtommath/mp_to_ubin.c @@ -0,0 +1,37 @@ +#include "tommath_private.h" +#ifdef MP_TO_UBIN_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* store in unsigned [big endian] format */ +mp_err mp_to_ubin(const mp_int *a, uint8_t *buf, size_t maxlen, size_t *written) +{ + size_t x, count; + mp_err err; + mp_int t; + + count = mp_ubin_size(a); + if (count > maxlen) { + return MP_BUF; + } + + if ((err = mp_init_copy(&t, a)) != MP_OKAY) { + return err; + } + + for (x = count; x --> 0u;) { + buf[x] = (uint8_t)(t.dp[0] & 255u); + if ((err = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) { + goto LBL_ERR; + } + } + + if (written != NULL) { + *written = count; + } + +LBL_ERR: + mp_clear(&t); + return err; +} +#endif diff --git a/src/libtommath/mp_ubin_size.c b/src/libtommath/mp_ubin_size.c new file mode 100644 index 000000000..4f16404f4 --- /dev/null +++ b/src/libtommath/mp_ubin_size.c @@ -0,0 +1,12 @@ +#include "tommath_private.h" +#ifdef MP_UBIN_SIZE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* get the size for an unsigned equivalent */ +size_t mp_ubin_size(const mp_int *a) +{ + size_t size = (size_t)mp_count_bits(a); + return (size / 8u) + (((size & 7u) != 0u) ? 1u : 0u); +} +#endif diff --git a/src/libtommath/mp_unpack.c b/src/libtommath/mp_unpack.c new file mode 100644 index 000000000..f0127fa44 --- /dev/null +++ b/src/libtommath/mp_unpack.c @@ -0,0 +1,49 @@ +#include "tommath_private.h" +#ifdef MP_UNPACK_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* based on gmp's mpz_import. + * see http://gmplib.org/manual/Integer-Import-and-Export.html + */ +mp_err mp_unpack(mp_int *rop, size_t count, mp_order order, size_t size, + mp_endian endian, size_t nails, const void *op) +{ + mp_err err; + size_t odd_nails, nail_bytes, i, j; + uint8_t odd_nail_mask; + + mp_zero(rop); + + if (endian == MP_NATIVE_ENDIAN) { + MP_GET_ENDIANNESS(endian); + } + + odd_nails = (nails % 8u); + odd_nail_mask = 0xff; + for (i = 0; i < odd_nails; ++i) { + odd_nail_mask ^= (uint8_t)(1u << (7u - i)); + } + nail_bytes = nails / 8u; + + for (i = 0; i < count; ++i) { + for (j = 0; j < (size - nail_bytes); ++j) { + uint8_t byte = *((const uint8_t *)op + + (((order == MP_MSB_FIRST) ? i : ((count - 1u) - i)) * size) + + ((endian == MP_BIG_ENDIAN) ? (j + nail_bytes) : (((size - 1u) - j) - nail_bytes))); + + if ((err = mp_mul_2d(rop, (j == 0u) ? (int)(8u - odd_nails) : 8, rop)) != MP_OKAY) { + return err; + } + + rop->dp[0] |= (j == 0u) ? (mp_digit)(byte & odd_nail_mask) : (mp_digit)byte; + rop->used += 1; + } + } + + mp_clamp(rop); + + return MP_OKAY; +} + +#endif diff --git a/src/libtommath/mp_xor.c b/src/libtommath/mp_xor.c new file mode 100644 index 000000000..ff264192c --- /dev/null +++ b/src/libtommath/mp_xor.c @@ -0,0 +1,54 @@ +#include "tommath_private.h" +#ifdef MP_XOR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* two complement xor */ +mp_err mp_xor(const mp_int *a, const mp_int *b, mp_int *c) +{ + int used = MP_MAX(a->used, b->used) + 1, i; + mp_err err; + mp_digit ac = 1, bc = 1, cc = 1; + bool neg = (a->sign != b->sign); + + if ((err = mp_grow(c, used)) != MP_OKAY) { + return err; + } + + for (i = 0; i < used; i++) { + mp_digit x, y; + + /* convert to two complement if negative */ + if (mp_isneg(a)) { + ac += (i >= a->used) ? MP_MASK : (~a->dp[i] & MP_MASK); + x = ac & MP_MASK; + ac >>= MP_DIGIT_BIT; + } else { + x = (i >= a->used) ? 0uL : a->dp[i]; + } + + /* convert to two complement if negative */ + if (mp_isneg(b)) { + bc += (i >= b->used) ? MP_MASK : (~b->dp[i] & MP_MASK); + y = bc & MP_MASK; + bc >>= MP_DIGIT_BIT; + } else { + y = (i >= b->used) ? 0uL : b->dp[i]; + } + + c->dp[i] = x ^ y; + + /* convert to to sign-magnitude if negative */ + if (neg) { + cc += ~c->dp[i] & MP_MASK; + c->dp[i] = cc & MP_MASK; + cc >>= MP_DIGIT_BIT; + } + } + + c->used = used; + c->sign = (neg ? MP_NEG : MP_ZPOS); + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/mp_zero.c b/src/libtommath/mp_zero.c new file mode 100644 index 000000000..48b60e458 --- /dev/null +++ b/src/libtommath/mp_zero.c @@ -0,0 +1,13 @@ +#include "tommath_private.h" +#ifdef MP_ZERO_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* set to zero */ +void mp_zero(mp_int *a) +{ + a->sign = MP_ZPOS; + s_mp_zero_digs(a->dp, a->used); + a->used = 0; +} +#endif diff --git a/src/libtommath/s_mp_add.c b/src/libtommath/s_mp_add.c new file mode 100644 index 000000000..2bda2fe1b --- /dev/null +++ b/src/libtommath/s_mp_add.c @@ -0,0 +1,70 @@ +#include "tommath_private.h" +#ifdef S_MP_ADD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* low level addition, based on HAC pp.594, Algorithm 14.7 */ +mp_err s_mp_add(const mp_int *a, const mp_int *b, mp_int *c) +{ + int oldused, min, max, i; + mp_digit u; + mp_err err; + + /* 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) { + MP_EXCH(const mp_int *, a, b); + } + + min = b->used; + max = a->used; + + /* init result */ + if ((err = mp_grow(c, max + 1)) != MP_OKAY) { + return err; + } + + /* get old used digit count and set new one */ + oldused = c->used; + c->used = max + 1; + + /* zero the carry */ + u = 0; + for (i = 0; i < min; i++) { + /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ + c->dp[i] = a->dp[i] + b->dp[i] + u; + + /* U = carry bit of T[i] */ + u = c->dp[i] >> (mp_digit)MP_DIGIT_BIT; + + /* take away carry bit from T[i] */ + c->dp[i] &= MP_MASK; + } + + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in + */ + if (min != max) { + for (; i < max; i++) { + /* T[i] = A[i] + U */ + c->dp[i] = a->dp[i] + u; + + /* U = carry bit of T[i] */ + u = c->dp[i] >> (mp_digit)MP_DIGIT_BIT; + + /* take away carry bit from T[i] */ + c->dp[i] &= MP_MASK; + } + } + + /* add carry */ + c->dp[i] = u; + + /* clear digits above oldused */ + s_mp_zero_digs(c->dp + c->used, oldused - c->used); + + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_copy_digs.c b/src/libtommath/s_mp_copy_digs.c new file mode 100644 index 000000000..4079c33a6 --- /dev/null +++ b/src/libtommath/s_mp_copy_digs.c @@ -0,0 +1,23 @@ +#include "tommath_private.h" +#ifdef S_MP_COPY_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifdef MP_USE_MEMOPS +# include +#endif + +void s_mp_copy_digs(mp_digit *d, const mp_digit *s, int digits) +{ +#ifdef MP_USE_MEMOPS + if (digits > 0) { + memcpy(d, s, (size_t)digits * sizeof(mp_digit)); + } +#else + while (digits-- > 0) { + *d++ = *s++; + } +#endif +} + +#endif diff --git a/src/libtommath/s_mp_div_3.c b/src/libtommath/s_mp_div_3.c new file mode 100644 index 000000000..1cc6d3d8c --- /dev/null +++ b/src/libtommath/s_mp_div_3.c @@ -0,0 +1,64 @@ +#include "tommath_private.h" +#ifdef S_MP_DIV_3_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* divide by three (based on routine from MPI and the GMP manual) */ +mp_err s_mp_div_3(const mp_int *a, mp_int *c, mp_digit *d) +{ + mp_int q; + mp_word w; + mp_digit b; + mp_err err; + int ix; + + /* b = 2**MP_DIGIT_BIT / 3 */ + b = ((mp_word)1 << (mp_word)MP_DIGIT_BIT) / (mp_word)3; + + if ((err = mp_init_size(&q, a->used)) != MP_OKAY) { + return err; + } + + q.used = a->used; + q.sign = a->sign; + w = 0; + for (ix = a->used; ix --> 0;) { + mp_word t; + w = (w << (mp_word)MP_DIGIT_BIT) | (mp_word)a->dp[ix]; + + if (w >= 3u) { + /* multiply w by [1/3] */ + t = (w * (mp_word)b) >> (mp_word)MP_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 >= 3u) { + t += 1u; + w -= 3u; + } + } 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 MP_OKAY; +} + +#endif diff --git a/src/libtommath/s_mp_div_recursive.c b/src/libtommath/s_mp_div_recursive.c new file mode 100644 index 000000000..d719c4e28 --- /dev/null +++ b/src/libtommath/s_mp_div_recursive.c @@ -0,0 +1,159 @@ +#include "tommath_private.h" +#ifdef S_MP_DIV_RECURSIVE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* + Direct implementation of algorithms 1.8 "RecursiveDivRem" and 1.9 "UnbalancedDivision" + from: + + Brent, Richard P., and Paul Zimmermann. "Modern computer arithmetic" + Vol. 18. Cambridge University Press, 2010 + Available online at https://arxiv.org/pdf/1004.4710 + + pages 19ff. in the above online document. +*/ + +static mp_err s_recursion(const mp_int *a, const mp_int *b, mp_int *q, mp_int *r) +{ + mp_err err; + mp_int A1, A2, B1, B0, Q1, Q0, R1, R0, t; + int m = a->used - b->used, k = m/2; + + if (m < (MP_MUL_KARATSUBA_CUTOFF)) { + return s_mp_div_school(a, b, q, r); + } + + if ((err = mp_init_multi(&A1, &A2, &B1, &B0, &Q1, &Q0, &R1, &R0, &t, NULL)) != MP_OKAY) { + goto LBL_ERR; + } + + /* B1 = b / beta^k, B0 = b % beta^k*/ + if ((err = mp_div_2d(b, k * MP_DIGIT_BIT, &B1, &B0)) != MP_OKAY) goto LBL_ERR; + + /* (Q1, R1) = RecursiveDivRem(A / beta^(2k), B1) */ + if ((err = mp_div_2d(a, 2*k * MP_DIGIT_BIT, &A1, &t)) != MP_OKAY) goto LBL_ERR; + if ((err = s_recursion(&A1, &B1, &Q1, &R1)) != MP_OKAY) goto LBL_ERR; + + /* A1 = (R1 * beta^(2k)) + (A % beta^(2k)) - (Q1 * B0 * beta^k) */ + if ((err = mp_lshd(&R1, 2*k)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&R1, &t, &A1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_mul(&Q1, &B0, &t)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_lshd(&t, k)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_sub(&A1, &t, &A1)) != MP_OKAY) goto LBL_ERR; + + /* while A1 < 0 do Q1 = Q1 - 1, A1 = A1 + (beta^k * B) */ + if (mp_cmp_d(&A1, 0uL) == MP_LT) { + if ((err = mp_mul_2d(b, k * MP_DIGIT_BIT, &t)) != MP_OKAY) goto LBL_ERR; + do { + if ((err = mp_decr(&Q1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&A1, &t, &A1)) != MP_OKAY) goto LBL_ERR; + } while (mp_cmp_d(&A1, 0uL) == MP_LT); + } + /* (Q0, R0) = RecursiveDivRem(A1 / beta^(k), B1) */ + if ((err = mp_div_2d(&A1, k * MP_DIGIT_BIT, &A1, &t)) != MP_OKAY) goto LBL_ERR; + if ((err = s_recursion(&A1, &B1, &Q0, &R0)) != MP_OKAY) goto LBL_ERR; + + /* A2 = (R0*beta^k) + (A1 % beta^k) - (Q0*B0) */ + if ((err = mp_lshd(&R0, k)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&R0, &t, &A2)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_mul(&Q0, &B0, &t)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_sub(&A2, &t, &A2)) != MP_OKAY) goto LBL_ERR; + + /* while A2 < 0 do Q0 = Q0 - 1, A2 = A2 + B */ + while (mp_cmp_d(&A2, 0uL) == MP_LT) { + if ((err = mp_decr(&Q0)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&A2, b, &A2)) != MP_OKAY) goto LBL_ERR; + } + /* return q = (Q1*beta^k) + Q0, r = A2 */ + if ((err = mp_lshd(&Q1, k)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&Q1, &Q0, q)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_copy(&A2, r)) != MP_OKAY) goto LBL_ERR; + +LBL_ERR: + mp_clear_multi(&A1, &A2, &B1, &B0, &Q1, &Q0, &R1, &R0, &t, NULL); + return err; +} + + +mp_err s_mp_div_recursive(const mp_int *a, const mp_int *b, mp_int *q, mp_int *r) +{ + int j, m, n, sigma; + mp_err err; + bool neg; + mp_digit msb_b, msb; + mp_int A, B, Q, Q1, R, A_div, A_mod; + + if ((err = mp_init_multi(&A, &B, &Q, &Q1, &R, &A_div, &A_mod, NULL)) != MP_OKAY) { + goto LBL_ERR; + } + + /* most significant bit of a limb */ + /* assumes MP_DIGIT_MAX < (sizeof(mp_digit) * CHAR_BIT) */ + msb = (MP_DIGIT_MAX + (mp_digit)(1)) >> 1; + sigma = 0; + msb_b = b->dp[b->used - 1]; + while (msb_b < msb) { + sigma++; + msb_b <<= 1; + } + /* Use that sigma to normalize B */ + if ((err = mp_mul_2d(b, sigma, &B)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_mul_2d(a, sigma, &A)) != MP_OKAY) { + goto LBL_ERR; + } + + /* fix the sign */ + neg = (a->sign != b->sign); + A.sign = B.sign = MP_ZPOS; + + /* + If the magnitude of "A" is not more more than twice that of "B" we can work + on them directly, otherwise we need to work at "A" in chunks + */ + n = B.used; + m = A.used - B.used; + + /* Q = 0 */ + mp_zero(&Q); + while (m > n) { + /* (q, r) = RecursiveDivRem(A / (beta^(m-n)), B) */ + j = (m - n) * MP_DIGIT_BIT; + if ((err = mp_div_2d(&A, j, &A_div, &A_mod)) != MP_OKAY) goto LBL_ERR; + if ((err = s_recursion(&A_div, &B, &Q1, &R)) != MP_OKAY) goto LBL_ERR; + /* Q = (Q*beta!(n)) + q */ + if ((err = mp_mul_2d(&Q, n * MP_DIGIT_BIT, &Q)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&Q, &Q1, &Q)) != MP_OKAY) goto LBL_ERR; + /* A = (r * beta^(m-n)) + (A % beta^(m-n))*/ + if ((err = mp_mul_2d(&R, (m - n) * MP_DIGIT_BIT, &R)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&R, &A_mod, &A)) != MP_OKAY) goto LBL_ERR; + /* m = m - n */ + m = m - n; + } + /* (q, r) = RecursiveDivRem(A, B) */ + if ((err = s_recursion(&A, &B, &Q1, &R)) != MP_OKAY) goto LBL_ERR; + /* Q = (Q * beta^m) + q, R = r */ + if ((err = mp_mul_2d(&Q, m * MP_DIGIT_BIT, &Q)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&Q, &Q1, &Q)) != MP_OKAY) goto LBL_ERR; + + /* get sign before writing to c */ + R.sign = (mp_iszero(&Q) ? MP_ZPOS : a->sign); + + if (q != NULL) { + mp_exch(&Q, q); + q->sign = (neg ? MP_NEG : MP_ZPOS); + } + if (r != NULL) { + /* de-normalize the remainder */ + if ((err = mp_div_2d(&R, sigma, &R, NULL)) != MP_OKAY) goto LBL_ERR; + mp_exch(&R, r); + } +LBL_ERR: + mp_clear_multi(&A, &B, &Q, &Q1, &R, &A_div, &A_mod, NULL); + return err; +} + +#endif diff --git a/src/libtommath/s_mp_div_school.c b/src/libtommath/s_mp_div_school.c new file mode 100644 index 000000000..b6a615d10 --- /dev/null +++ b/src/libtommath/s_mp_div_school.c @@ -0,0 +1,156 @@ +#include "tommath_private.h" +#ifdef S_MP_DIV_SCHOOL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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. +*/ +mp_err s_mp_div_school(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d) +{ + mp_int q, x, y, t1, t2; + int n, t, i, norm; + bool neg; + mp_err err; + + if ((err = mp_init_size(&q, a->used + 2)) != MP_OKAY) { + return err; + } + q.used = a->used + 2; + + if ((err = mp_init(&t1)) != MP_OKAY) goto LBL_Q; + if ((err = mp_init(&t2)) != MP_OKAY) goto LBL_T1; + if ((err = mp_init_copy(&x, a)) != MP_OKAY) goto LBL_T2; + if ((err = mp_init_copy(&y, b)) != MP_OKAY) goto LBL_X; + + /* fix the sign */ + neg = (a->sign != b->sign); + x.sign = y.sign = MP_ZPOS; + + /* normalize both x and y, ensure that y >= b/2, [b == 2**MP_DIGIT_BIT] */ + norm = mp_count_bits(&y) % MP_DIGIT_BIT; + if (norm < (MP_DIGIT_BIT - 1)) { + norm = (MP_DIGIT_BIT - 1) - norm; + if ((err = mp_mul_2d(&x, norm, &x)) != MP_OKAY) goto LBL_Y; + if ((err = mp_mul_2d(&y, norm, &y)) != MP_OKAY) goto LBL_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} } */ + /* y = y*b**{n-t} */ + if ((err = mp_lshd(&y, n - t)) != MP_OKAY) goto LBL_Y; + + while (mp_cmp(&x, &y) != MP_LT) { + ++(q.dp[n - t]); + if ((err = mp_sub(&x, &y, &x)) != MP_OKAY) goto LBL_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 << (mp_digit)MP_DIGIT_BIT) - (mp_digit)1; + } else { + mp_word tmp; + tmp = (mp_word)x.dp[i] << (mp_word)MP_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] + 1uL) & (mp_digit)MP_MASK; + do { + q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1uL) & (mp_digit)MP_MASK; + + /* find left hand */ + mp_zero(&t1); + t1.dp[0] = ((t - 1) < 0) ? 0u : y.dp[t - 1]; + t1.dp[1] = y.dp[t]; + t1.used = 2; + if ((err = mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) goto LBL_Y; + + /* find right hand */ + t2.dp[0] = ((i - 2) < 0) ? 0u : x.dp[i - 2]; + t2.dp[1] = x.dp[i - 1]; /* i >= 1 always holds */ + 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 ((err = mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) goto LBL_Y; + if ((err = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) goto LBL_Y; + if ((err = mp_sub(&x, &t1, &x)) != MP_OKAY) goto LBL_Y; + + /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ + if (mp_isneg(&x)) { + if ((err = mp_copy(&y, &t1)) != MP_OKAY) goto LBL_Y; + if ((err = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) goto LBL_Y; + if ((err = mp_add(&x, &t1, &x)) != MP_OKAY) goto LBL_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 = mp_iszero(&x) ? MP_ZPOS : a->sign; + + if (c != NULL) { + mp_clamp(&q); + mp_exch(&q, c); + c->sign = (neg ? MP_NEG : MP_ZPOS); + } + + if (d != NULL) { + if ((err = mp_div_2d(&x, norm, &x, NULL)) != MP_OKAY) goto LBL_Y; + mp_exch(&x, d); + } + +LBL_Y: + mp_clear(&y); +LBL_X: + mp_clear(&x); +LBL_T2: + mp_clear(&t2); +LBL_T1: + mp_clear(&t1); +LBL_Q: + mp_clear(&q); + return err; +} + +#endif diff --git a/src/libtommath/s_mp_div_small.c b/src/libtommath/s_mp_div_small.c new file mode 100644 index 000000000..2d951be1c --- /dev/null +++ b/src/libtommath/s_mp_div_small.c @@ -0,0 +1,51 @@ +#include "tommath_private.h" +#ifdef S_MP_DIV_SMALL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* slower bit-bang division... also smaller */ +mp_err s_mp_div_small(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d) +{ + mp_int ta, tb, tq, q; + int n; + bool neg; + mp_err err; + + /* init our temps */ + if ((err = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { + return err; + } + + mp_set(&tq, 1uL); + n = mp_count_bits(a) - mp_count_bits(b); + if ((err = mp_abs(a, &ta)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_abs(b, &tb)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_mul_2d(&tq, n, &tq)) != MP_OKAY) goto LBL_ERR; + + while (n-- >= 0) { + if (mp_cmp(&tb, &ta) != MP_GT) { + if ((err = mp_sub(&ta, &tb, &ta)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&q, &tq, &q)) != MP_OKAY) goto LBL_ERR; + } + if ((err = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY) goto LBL_ERR; + } + + /* now q == quotient and ta == remainder */ + + neg = (a->sign != b->sign); + if (c != NULL) { + mp_exch(c, &q); + c->sign = ((neg && !mp_iszero(c)) ? MP_NEG : MP_ZPOS); + } + if (d != NULL) { + mp_exch(d, &ta); + d->sign = (mp_iszero(d) ? MP_ZPOS : a->sign); + } +LBL_ERR: + mp_clear_multi(&ta, &tb, &tq, &q, NULL); + return err; +} + +#endif diff --git a/src/libtommath/s_mp_exptmod.c b/src/libtommath/s_mp_exptmod.c new file mode 100644 index 000000000..2a89a2cbf --- /dev/null +++ b/src/libtommath/s_mp_exptmod.c @@ -0,0 +1,198 @@ +#include "tommath_private.h" +#ifdef S_MP_EXPTMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifdef MP_LOW_MEM +# define TAB_SIZE 32 +# define MAX_WINSIZE 5 +#else +# define TAB_SIZE 256 +# define MAX_WINSIZE 0 +#endif + +mp_err s_mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode) +{ + mp_int M[TAB_SIZE], res, mu; + mp_digit buf; + mp_err err; + int bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + mp_err(*redux)(mp_int *x, const mp_int *m, const mp_int *mu); + + /* 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; + } + + winsize = MAX_WINSIZE ? MP_MIN(MAX_WINSIZE, winsize) : winsize; + + /* 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 LBL_M; + + if (redmode == 0) { + if ((err = mp_reduce_setup(&mu, P)) != MP_OKAY) goto LBL_MU; + redux = mp_reduce; + } else { + if ((err = mp_reduce_2k_setup_l(P, &mu)) != MP_OKAY) goto LBL_MU; + redux = mp_reduce_2k_l; + } + + /* 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 LBL_MU; + + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times + */ + if ((err = mp_copy(&M[1], &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) goto LBL_MU; + + for (x = 0; x < (winsize - 1); x++) { + /* square it */ + if ((err = mp_sqr(&M[(size_t)1 << (winsize - 1)], + &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) goto LBL_MU; + + /* reduce modulo P */ + if ((err = redux(&M[(size_t)1 << (winsize - 1)], P, &mu)) != MP_OKAY) goto LBL_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 LBL_MU; + if ((err = redux(&M[x], P, &mu)) != MP_OKAY) goto LBL_MU; + } + + /* setup result */ + if ((err = mp_init(&res)) != MP_OKAY) goto LBL_MU; + mp_set(&res, 1uL); + + /* 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)MP_DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (buf >> (mp_digit)(MP_DIGIT_BIT - 1)) & 1uL; + 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 LBL_RES; + if ((err = redux(&res, P, &mu)) != MP_OKAY) goto LBL_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 LBL_RES; + if ((err = redux(&res, P, &mu)) != MP_OKAY) goto LBL_RES; + } + + /* then multiply */ + if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) goto LBL_RES; + if ((err = redux(&res, P, &mu)) != MP_OKAY) goto LBL_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 LBL_RES; + if ((err = redux(&res, P, &mu)) != MP_OKAY) goto LBL_RES; + + bitbuf <<= 1; + if ((bitbuf & (1 << winsize)) != 0) { + /* then multiply */ + if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) goto LBL_RES; + if ((err = redux(&res, P, &mu)) != MP_OKAY) goto LBL_RES; + } + } + } + + mp_exch(&res, Y); + err = MP_OKAY; +LBL_RES: + mp_clear(&res); +LBL_MU: + mp_clear(&mu); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear(&M[x]); + } + return err; +} +#endif diff --git a/src/libtommath/s_mp_exptmod_fast.c b/src/libtommath/s_mp_exptmod_fast.c new file mode 100644 index 000000000..e7729f49d --- /dev/null +++ b/src/libtommath/s_mp_exptmod_fast.c @@ -0,0 +1,254 @@ +#include "tommath_private.h" +#ifdef S_MP_EXPTMOD_FAST_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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 +# define MAX_WINSIZE 5 +#else +# define TAB_SIZE 256 +# define MAX_WINSIZE 0 +#endif + +mp_err s_mp_exptmod_fast(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode) +{ + mp_int M[TAB_SIZE], res; + mp_digit buf, mp; + int bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; + mp_err err; + + /* 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. + */ + mp_err(*redux)(mp_int *x, const mp_int *n, mp_digit rho); + + /* 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; + } + + winsize = MAX_WINSIZE ? MP_MIN(MAX_WINSIZE, winsize) : winsize; + + /* init M array */ + /* init first cell */ + if ((err = mp_init_size(&M[1], P->alloc)) != 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_size(&M[x], P->alloc)) != 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) { + if (MP_HAS(MP_MONTGOMERY_SETUP)) { + /* now setup montgomery */ + if ((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) goto LBL_M; + } else { + err = MP_VAL; + goto LBL_M; + } + + /* automatically pick the comba one if available (saves quite a few calls/ifs) */ + if (MP_HAS(S_MP_MONTGOMERY_REDUCE_COMBA) && + (((P->used * 2) + 1) < MP_WARRAY) && + (P->used < MP_MAX_COMBA)) { + redux = s_mp_montgomery_reduce_comba; + } else if (MP_HAS(MP_MONTGOMERY_REDUCE)) { + /* use slower baseline Montgomery method */ + redux = mp_montgomery_reduce; + } else { + err = MP_VAL; + goto LBL_M; + } + } else if (redmode == 1) { + if (MP_HAS(MP_DR_SETUP) && MP_HAS(MP_DR_REDUCE)) { + /* setup DR reduction for moduli of the form B**k - b */ + mp_dr_setup(P, &mp); + redux = mp_dr_reduce; + } else { + err = MP_VAL; + goto LBL_M; + } + } else if (MP_HAS(MP_REDUCE_2K_SETUP) && MP_HAS(MP_REDUCE_2K)) { + /* setup DR reduction for moduli of the form 2**k - b */ + if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) goto LBL_M; + redux = mp_reduce_2k; + } else { + err = MP_VAL; + goto LBL_M; + } + + /* setup result */ + if ((err = mp_init_size(&res, P->alloc)) != MP_OKAY) goto LBL_M; + + /* create M table + * + + * + * The first half of the table is not computed though accept for M[0] and M[1] + */ + + if (redmode == 0) { + if (MP_HAS(MP_MONTGOMERY_CALC_NORMALIZATION)) { + /* now we need R mod m */ + if ((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) goto LBL_RES; + + /* now set M[1] to G * R mod m */ + if ((err = mp_mulmod(G, &res, P, &M[1])) != MP_OKAY) goto LBL_RES; + } else { + err = MP_VAL; + goto LBL_RES; + } + } else { + mp_set(&res, 1uL); + if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) goto LBL_RES; + } + + /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ + if ((err = mp_copy(&M[1], &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) goto LBL_RES; + + for (x = 0; x < (winsize - 1); x++) { + if ((err = mp_sqr(&M[(size_t)1 << (winsize - 1)], &M[(size_t)1 << (winsize - 1)])) != MP_OKAY) goto LBL_RES; + if ((err = redux(&M[(size_t)1 << (winsize - 1)], P, mp)) != MP_OKAY) goto LBL_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 LBL_RES; + if ((err = redux(&M[x], P, mp)) != MP_OKAY) goto LBL_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)MP_DIGIT_BIT; + } + + /* grab the next msb from the exponent */ + y = (mp_digit)(buf >> (MP_DIGIT_BIT - 1)) & 1uL; + 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 LBL_RES; + if ((err = redux(&res, P, mp)) != MP_OKAY) goto LBL_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 LBL_RES; + if ((err = redux(&res, P, mp)) != MP_OKAY) goto LBL_RES; + } + + /* then multiply */ + if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) goto LBL_RES; + if ((err = redux(&res, P, mp)) != MP_OKAY) goto LBL_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 LBL_RES; + if ((err = redux(&res, P, mp)) != MP_OKAY) goto LBL_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 LBL_RES; + if ((err = redux(&res, P, mp)) != MP_OKAY) goto LBL_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 = redux(&res, P, mp)) != MP_OKAY) goto LBL_RES; + } + + /* swap res with Y */ + mp_exch(&res, Y); + err = MP_OKAY; +LBL_RES: + mp_clear(&res); +LBL_M: + mp_clear(&M[1]); + for (x = 1<<(winsize-1); x < (1 << winsize); x++) { + mp_clear(&M[x]); + } + return err; +} +#endif diff --git a/src/libtommath/s_mp_get_bit.c b/src/libtommath/s_mp_get_bit.c new file mode 100644 index 000000000..a509bcecb --- /dev/null +++ b/src/libtommath/s_mp_get_bit.c @@ -0,0 +1,21 @@ +#include "tommath_private.h" +#ifdef S_MP_GET_BIT_C + +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* Get bit at position b and return true if the bit is 1, false if it is 0 */ +bool s_mp_get_bit(const mp_int *a, int b) +{ + mp_digit bit; + int limb = b / MP_DIGIT_BIT; + + if (limb < 0 || limb >= a->used) { + return false; + } + + bit = (mp_digit)1 << (b % MP_DIGIT_BIT); + return ((a->dp[limb] & bit) != 0u); +} + +#endif diff --git a/src/libtommath/s_mp_invmod.c b/src/libtommath/s_mp_invmod.c new file mode 100644 index 000000000..f3b3f436f --- /dev/null +++ b/src/libtommath/s_mp_invmod.c @@ -0,0 +1,117 @@ +#include "tommath_private.h" +#ifdef S_MP_INVMOD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* hac 14.61, pp608 */ +mp_err s_mp_invmod(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_int x, y, u, v, A, B, C, D; + mp_err err; + + /* b cannot be negative */ + if ((b->sign == MP_NEG) || mp_iszero(b)) { + return MP_VAL; + } + + /* init temps */ + if ((err = mp_init_multi(&x, &y, &u, &v, + &A, &B, &C, &D, NULL)) != MP_OKAY) { + return err; + } + + /* x = a, y = b */ + if ((err = mp_mod(a, b, &x)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_copy(b, &y)) != MP_OKAY) goto LBL_ERR; + + /* 2. [modified] if x,y are both even then return an error! */ + if (mp_iseven(&x) && mp_iseven(&y)) { + err = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((err = mp_copy(&x, &u)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_copy(&y, &v)) != MP_OKAY) goto LBL_ERR; + mp_set(&A, 1uL); + mp_set(&D, 1uL); + + do { + /* 4. while u is even do */ + while (mp_iseven(&u)) { + /* 4.1 u = u/2 */ + if ((err = mp_div_2(&u, &u)) != MP_OKAY) goto LBL_ERR; + + /* 4.2 if A or B is odd then */ + if (mp_isodd(&A) || mp_isodd(&B)) { + /* A = (A+y)/2, B = (B-x)/2 */ + if ((err = mp_add(&A, &y, &A)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_sub(&B, &x, &B)) != MP_OKAY) goto LBL_ERR; + } + /* A = A/2, B = B/2 */ + if ((err = mp_div_2(&A, &A)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_div_2(&B, &B)) != MP_OKAY) goto LBL_ERR; + } + + /* 5. while v is even do */ + while (mp_iseven(&v)) { + /* 5.1 v = v/2 */ + if ((err = mp_div_2(&v, &v)) != MP_OKAY) goto LBL_ERR; + + /* 5.2 if C or D is odd then */ + if (mp_isodd(&C) || mp_isodd(&D)) { + /* C = (C+y)/2, D = (D-x)/2 */ + if ((err = mp_add(&C, &y, &C)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_sub(&D, &x, &D)) != MP_OKAY) goto LBL_ERR; + } + /* C = C/2, D = D/2 */ + if ((err = mp_div_2(&C, &C)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_div_2(&D, &D)) != MP_OKAY) goto LBL_ERR; + } + + /* 6. if u >= v then */ + if (mp_cmp(&u, &v) != MP_LT) { + /* u = u - v, A = A - C, B = B - D */ + if ((err = mp_sub(&u, &v, &u)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_sub(&A, &C, &A)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_sub(&B, &D, &B)) != MP_OKAY) goto LBL_ERR; + } else { + /* v - v - u, C = C - A, D = D - B */ + if ((err = mp_sub(&v, &u, &v)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_sub(&C, &A, &C)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_sub(&D, &B, &D)) != MP_OKAY) goto LBL_ERR; + } + + /* if not zero goto step 4 */ + } while (!mp_iszero(&u)); + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d(&v, 1uL) != MP_EQ) { + err = MP_VAL; + goto LBL_ERR; + } + + /* if its too low */ + while (mp_cmp_d(&C, 0uL) == MP_LT) { + if ((err = mp_add(&C, b, &C)) != MP_OKAY) goto LBL_ERR; + } + + /* too big */ + while (mp_cmp_mag(&C, b) != MP_LT) { + if ((err = mp_sub(&C, b, &C)) != MP_OKAY) goto LBL_ERR; + } + + /* C is now the inverse */ + mp_exch(&C, c); + +LBL_ERR: + mp_clear_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL); + return err; +} +#endif diff --git a/src/libtommath/s_mp_invmod_odd.c b/src/libtommath/s_mp_invmod_odd.c new file mode 100644 index 000000000..e12dfa17f --- /dev/null +++ b/src/libtommath/s_mp_invmod_odd.c @@ -0,0 +1,116 @@ +#include "tommath_private.h" +#ifdef S_MP_INVMOD_ODD_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* computes the modular inverse via binary extended euclidean algorithm, + * that is c = 1/a mod b + * + * Based on slow invmod except this is optimized for the case where b is + * odd as per HAC Note 14.64 on pp. 610 + */ +mp_err s_mp_invmod_odd(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_int x, y, u, v, B, D; + mp_sign sign; + mp_err err; + + /* 2. [modified] b must be odd */ + if (mp_iseven(b)) { + return MP_VAL; + } + + /* init all our temps */ + if ((err = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { + return err; + } + + /* x == modulus, y == value to invert */ + if ((err = mp_copy(b, &x)) != MP_OKAY) goto LBL_ERR; + + /* we need y = |a| */ + if ((err = mp_mod(a, b, &y)) != MP_OKAY) goto LBL_ERR; + + /* if one of x,y is zero return an error! */ + if (mp_iszero(&x) || mp_iszero(&y)) { + err = MP_VAL; + goto LBL_ERR; + } + + /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ + if ((err = mp_copy(&x, &u)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_copy(&y, &v)) != MP_OKAY) goto LBL_ERR; + mp_set(&D, 1uL); + + do { + /* 4. while u is even do */ + while (mp_iseven(&u)) { + /* 4.1 u = u/2 */ + if ((err = mp_div_2(&u, &u)) != MP_OKAY) goto LBL_ERR; + + /* 4.2 if B is odd then */ + if (mp_isodd(&B)) { + if ((err = mp_sub(&B, &x, &B)) != MP_OKAY) goto LBL_ERR; + } + /* B = B/2 */ + if ((err = mp_div_2(&B, &B)) != MP_OKAY) goto LBL_ERR; + } + + /* 5. while v is even do */ + while (mp_iseven(&v)) { + /* 5.1 v = v/2 */ + if ((err = mp_div_2(&v, &v)) != MP_OKAY) goto LBL_ERR; + + /* 5.2 if D is odd then */ + if (mp_isodd(&D)) { + /* D = (D-x)/2 */ + if ((err = mp_sub(&D, &x, &D)) != MP_OKAY) goto LBL_ERR; + } + /* D = D/2 */ + if ((err = mp_div_2(&D, &D)) != MP_OKAY) goto LBL_ERR; + } + + /* 6. if u >= v then */ + if (mp_cmp(&u, &v) != MP_LT) { + /* u = u - v, B = B - D */ + if ((err = mp_sub(&u, &v, &u)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_sub(&B, &D, &B)) != MP_OKAY) goto LBL_ERR; + } else { + /* v - v - u, D = D - B */ + if ((err = mp_sub(&v, &u, &v)) != MP_OKAY) goto LBL_ERR; + + if ((err = mp_sub(&D, &B, &D)) != MP_OKAY) goto LBL_ERR; + } + + /* if not zero goto step 4 */ + } while (!mp_iszero(&u)); + + /* now a = C, b = D, gcd == g*v */ + + /* if v != 1 then there is no inverse */ + if (mp_cmp_d(&v, 1uL) != MP_EQ) { + err = MP_VAL; + goto LBL_ERR; + } + + /* b is now the inverse */ + sign = a->sign; + while (mp_isneg(&D)) { + if ((err = mp_add(&D, b, &D)) != MP_OKAY) goto LBL_ERR; + } + + /* too big */ + while (mp_cmp_mag(&D, b) != MP_LT) { + if ((err = mp_sub(&D, b, &D)) != MP_OKAY) goto LBL_ERR; + } + + mp_exch(&D, c); + c->sign = sign; + err = MP_OKAY; + +LBL_ERR: + mp_clear_multi(&x, &y, &u, &v, &B, &D, NULL); + return err; +} +#endif diff --git a/src/libtommath/s_mp_log.c b/src/libtommath/s_mp_log.c new file mode 100644 index 000000000..d1ac73b1a --- /dev/null +++ b/src/libtommath/s_mp_log.c @@ -0,0 +1,81 @@ +#include "tommath_private.h" +#ifdef S_MP_LOG_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +mp_err s_mp_log(const mp_int *a, mp_digit base, int *c) +{ + mp_err err; + int high, low; + mp_int bracket_low, bracket_high, bracket_mid, t, bi_base; + + mp_ord cmp = mp_cmp_d(a, base); + if ((cmp == MP_LT) || (cmp == MP_EQ)) { + *c = cmp == MP_EQ; + return MP_OKAY; + } + + if ((err = + mp_init_multi(&bracket_low, &bracket_high, + &bracket_mid, &t, &bi_base, NULL)) != MP_OKAY) { + return err; + } + + low = 0; + mp_set(&bracket_low, 1uL); + high = 1; + + mp_set(&bracket_high, base); + + /* + A kind of Giant-step/baby-step algorithm. + Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/ + The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped + for small n. + */ + while (mp_cmp(&bracket_high, a) == MP_LT) { + low = high; + if ((err = mp_copy(&bracket_high, &bracket_low)) != MP_OKAY) { + goto LBL_END; + } + high <<= 1; + if ((err = mp_sqr(&bracket_high, &bracket_high)) != MP_OKAY) { + goto LBL_END; + } + } + mp_set(&bi_base, base); + + while ((high - low) > 1) { + int mid = (high + low) >> 1; + + if ((err = mp_expt_n(&bi_base, mid - low, &t)) != MP_OKAY) { + goto LBL_END; + } + if ((err = mp_mul(&bracket_low, &t, &bracket_mid)) != MP_OKAY) { + goto LBL_END; + } + cmp = mp_cmp(a, &bracket_mid); + if (cmp == MP_LT) { + high = mid; + mp_exch(&bracket_mid, &bracket_high); + } + if (cmp == MP_GT) { + low = mid; + mp_exch(&bracket_mid, &bracket_low); + } + if (cmp == MP_EQ) { + *c = mid; + goto LBL_END; + } + } + + *c = (mp_cmp(&bracket_high, a) == MP_EQ) ? high : low; + +LBL_END: + mp_clear_multi(&bracket_low, &bracket_high, &bracket_mid, + &t, &bi_base, NULL); + return err; +} + + +#endif diff --git a/src/libtommath/s_mp_log_2expt.c b/src/libtommath/s_mp_log_2expt.c new file mode 100644 index 000000000..ec0fda3b7 --- /dev/null +++ b/src/libtommath/s_mp_log_2expt.c @@ -0,0 +1,12 @@ +#include "tommath_private.h" +#ifdef S_MP_LOG_2EXPT_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +int s_mp_log_2expt(const mp_int *a, mp_digit base) +{ + int y; + for (y = 0; (base & 1) == 0; y++, base >>= 1) {} + return (mp_count_bits(a) - 1) / y; +} +#endif diff --git a/src/libtommath/s_mp_log_d.c b/src/libtommath/s_mp_log_d.c new file mode 100644 index 000000000..5ff6c1fba --- /dev/null +++ b/src/libtommath/s_mp_log_d.c @@ -0,0 +1,65 @@ +#include "tommath_private.h" +#ifdef S_MP_LOG_D_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +static mp_word s_pow(mp_word base, mp_word exponent) +{ + mp_word result = 1u; + while (exponent != 0u) { + if ((exponent & 1u) == 1u) { + result *= base; + } + exponent >>= 1; + base *= base; + } + + return result; +} + +int s_mp_log_d(mp_digit base, mp_digit n) +{ + mp_word bracket_low = 1uLL, bracket_high = base, N = n; + int ret, high = 1, low = 0; + + if (n < base) { + return 0; + } + if (n == base) { + return 1; + } + + while (bracket_high < N) { + low = high; + bracket_low = bracket_high; + high <<= 1; + bracket_high *= bracket_high; + } + + while (((mp_digit)(high - low)) > 1uL) { + int mid = (low + high) >> 1; + mp_word bracket_mid = bracket_low * s_pow(base, (mp_word)(mid - low)); + + if (N < bracket_mid) { + high = mid ; + bracket_high = bracket_mid ; + } + if (N > bracket_mid) { + low = mid ; + bracket_low = bracket_mid ; + } + if (N == bracket_mid) { + return mid; + } + } + + if (bracket_high == N) { + ret = high; + } else { + ret = low; + } + + return ret; +} + +#endif diff --git a/src/libtommath/s_mp_montgomery_reduce_comba.c b/src/libtommath/s_mp_montgomery_reduce_comba.c new file mode 100644 index 000000000..6f249c49f --- /dev/null +++ b/src/libtommath/s_mp_montgomery_reduce_comba.c @@ -0,0 +1,119 @@ +#include "tommath_private.h" +#ifdef S_MP_MONTGOMERY_REDUCE_COMBA_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* computes xR**-1 == x (mod N) via Montgomery Reduction + * + * This is an optimized implementation of 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. +*/ +mp_err s_mp_montgomery_reduce_comba(mp_int *x, const mp_int *n, mp_digit rho) +{ + int ix, oldused; + mp_err err; + mp_word W[MP_WARRAY]; + + if (x->used > MP_WARRAY) { + return MP_VAL; + } + + /* get old used count */ + oldused = x->used; + + /* grow a as required */ + if ((err = mp_grow(x, n->used + 1)) != MP_OKAY) { + return err; + } + + /* first we have to get the digits of the input into + * an array of double precision words W[...] + */ + + /* copy the digits of a into W[0..a->used-1] */ + for (ix = 0; ix < x->used; ix++) { + W[ix] = x->dp[ix]; + } + + /* zero the high words of W[a->used..m->used*2] */ + if (ix < ((n->used * 2) + 1)) { + s_mp_zero_buf(W + x->used, sizeof(mp_word) * (size_t)(((n->used * 2) + 1) - ix)); + } + + /* now we proceed to zero successive digits + * from the least significant upwards + */ + for (ix = 0; ix < n->used; ix++) { + int iy; + mp_digit mu; + + /* 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) + */ + mu = ((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 + */ + for (iy = 0; iy < n->used; iy++) { + W[ix + iy] += (mp_word)mu * (mp_word)n->dp[iy]; + } + + /* now fix carry for next digit, W[ix+1] */ + W[ix + 1] += W[ix] >> (mp_word)MP_DIGIT_BIT; + } + + /* now we have to propagate the carries and + * shift the words downward [all those least + * significant digits we zeroed]. + */ + + for (; ix < (n->used * 2); ix++) { + W[ix + 1] += W[ix] >> (mp_word)MP_DIGIT_BIT; + } + + /* copy out, A = A/b**n + * + * The result is A/b**n but instead of converting from an + * array of mp_word to mp_digit than calling mp_rshd + * we just copy them in the right order + */ + + for (ix = 0; ix < (n->used + 1); ix++) { + x->dp[ix] = W[n->used + ix] & (mp_word)MP_MASK; + } + + /* set the max used */ + x->used = n->used + 1; + + /* zero oldused digits, if the input a was larger than + * m->used+1 we'll have to clear the digits + */ + s_mp_zero_digs(x->dp + x->used, oldused - x->used); + + 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; +} +#endif diff --git a/src/libtommath/s_mp_mul.c b/src/libtommath/s_mp_mul.c new file mode 100644 index 000000000..fb99d8054 --- /dev/null +++ b/src/libtommath/s_mp_mul.c @@ -0,0 +1,61 @@ +#include "tommath_private.h" +#ifdef S_MP_MUL_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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. + */ +mp_err s_mp_mul(const mp_int *a, const mp_int *b, mp_int *c, int digs) +{ + mp_int t; + mp_err err; + int pa, ix; + + /* can we use the fast multiplier? */ + if ((digs < MP_WARRAY) && + (MP_MIN(a->used, b->used) < MP_MAX_COMBA)) { + return s_mp_mul_comba(a, b, c, digs); + } + + if ((err = mp_init_size(&t, digs)) != MP_OKAY) { + return err; + } + t.used = digs; + + /* compute the digits of the product directly */ + pa = a->used; + for (ix = 0; ix < pa; ix++) { + int iy, pb; + mp_digit u = 0; + + /* limit ourselves to making digs digits of output */ + pb = MP_MIN(b->used, digs - ix); + + /* compute the columns of the output and propagate the carry */ + for (iy = 0; iy < pb; iy++) { + /* compute the column as a mp_word */ + mp_word r = (mp_word)t.dp[ix + iy] + + ((mp_word)a->dp[ix] * (mp_word)b->dp[iy]) + + (mp_word)u; + + /* the new column is the lower part of the result */ + t.dp[ix + iy] = (mp_digit)(r & (mp_word)MP_MASK); + + /* get the carry word from the result */ + u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + } + /* set carry if it is placed below digs */ + if ((ix + iy) < digs) { + t.dp[ix + pb] = u; + } + } + + mp_clamp(&t); + mp_exch(&t, c); + + mp_clear(&t); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_mul_balance.c b/src/libtommath/s_mp_mul_balance.c new file mode 100644 index 000000000..f36f0d30b --- /dev/null +++ b/src/libtommath/s_mp_mul_balance.c @@ -0,0 +1,71 @@ +#include "tommath_private.h" +#ifdef S_MP_MUL_BALANCE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* single-digit multiplication with the smaller number as the single-digit */ +mp_err s_mp_mul_balance(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_int a0, tmp, r; + mp_err err; + int i, j, + nblocks = MP_MAX(a->used, b->used) / MP_MIN(a->used, b->used), + bsize = MP_MIN(a->used, b->used); + + if ((err = mp_init_size(&a0, bsize + 2)) != MP_OKAY) { + return err; + } + if ((err = mp_init_multi(&tmp, &r, NULL)) != MP_OKAY) { + mp_clear(&a0); + return err; + } + + /* Make sure that A is the larger one*/ + if (a->used < b->used) { + MP_EXCH(const mp_int *, a, b); + } + + for (i = 0, j=0; i < nblocks; i++) { + /* Cut a slice off of a */ + a0.used = bsize; + s_mp_copy_digs(a0.dp, a->dp + j, a0.used); + j += a0.used; + mp_clamp(&a0); + + /* Multiply with b */ + if ((err = mp_mul(&a0, b, &tmp)) != MP_OKAY) { + goto LBL_ERR; + } + /* Shift tmp to the correct position */ + if ((err = mp_lshd(&tmp, bsize * i)) != MP_OKAY) { + goto LBL_ERR; + } + /* Add to output. No carry needed */ + if ((err = mp_add(&r, &tmp, &r)) != MP_OKAY) { + goto LBL_ERR; + } + } + /* The left-overs; there are always left-overs */ + if (j < a->used) { + a0.used = a->used - j; + s_mp_copy_digs(a0.dp, a->dp + j, a0.used); + j += a0.used; + mp_clamp(&a0); + + if ((err = mp_mul(&a0, b, &tmp)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_lshd(&tmp, bsize * i)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_add(&r, &tmp, &r)) != MP_OKAY) { + goto LBL_ERR; + } + } + + mp_exch(&r,c); +LBL_ERR: + mp_clear_multi(&a0, &tmp, &r,NULL); + return err; +} +#endif diff --git a/src/libtommath/s_mp_mul_comba.c b/src/libtommath/s_mp_mul_comba.c new file mode 100644 index 000000000..07dd7913d --- /dev/null +++ b/src/libtommath/s_mp_mul_comba.c @@ -0,0 +1,78 @@ +#include "tommath_private.h" +#ifdef S_MP_MUL_COMBA_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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. + * + */ +mp_err s_mp_mul_comba(const mp_int *a, const mp_int *b, mp_int *c, int digs) +{ + int oldused, pa, ix; + mp_err err; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + if ((err = mp_grow(c, digs)) != MP_OKAY) { + return err; + } + + /* number of output digits to produce */ + pa = MP_MIN(digs, a->used + b->used); + + /* clear the carry */ + _W = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy, iz; + + /* get offsets into the two bignums */ + ty = MP_MIN(b->used-1, ix); + tx = ix - ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MP_MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; ++iz) { + _W += (mp_word)a->dp[tx + iz] * (mp_word)b->dp[ty - iz]; + } + + /* store term */ + W[ix] = (mp_digit)_W & MP_MASK; + + /* make next carry */ + _W = _W >> (mp_word)MP_DIGIT_BIT; + } + + /* setup dest */ + oldused = c->used; + c->used = pa; + + for (ix = 0; ix < pa; ix++) { + /* now extract the previous digit [below the carry] */ + c->dp[ix] = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + s_mp_zero_digs(c->dp + c->used, oldused - c->used); + + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_mul_high.c b/src/libtommath/s_mp_mul_high.c new file mode 100644 index 000000000..1bde00aa9 --- /dev/null +++ b/src/libtommath/s_mp_mul_high.c @@ -0,0 +1,52 @@ +#include "tommath_private.h" +#ifdef S_MP_MUL_HIGH_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* multiplies |a| * |b| and does not compute the lower digs digits + * [meant to get the higher part of the product] + */ +mp_err s_mp_mul_high(const mp_int *a, const mp_int *b, mp_int *c, int digs) +{ + mp_int t; + int pa, pb, ix; + mp_err err; + + /* can we use the fast multiplier? */ + if (MP_HAS(S_MP_MUL_HIGH_COMBA) + && ((a->used + b->used + 1) < MP_WARRAY) + && (MP_MIN(a->used, b->used) < MP_MAX_COMBA)) { + return s_mp_mul_high_comba(a, b, c, digs); + } + + if ((err = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) { + return err; + } + t.used = a->used + b->used + 1; + + pa = a->used; + pb = b->used; + for (ix = 0; ix < pa; ix++) { + int iy; + mp_digit u = 0; + + for (iy = digs - ix; iy < pb; iy++) { + /* calculate the double precision result */ + mp_word r = (mp_word)t.dp[ix + iy] + + ((mp_word)a->dp[ix] * (mp_word)b->dp[iy]) + + (mp_word)u; + + /* get the lower part */ + t.dp[ix + iy] = (mp_digit)(r & (mp_word)MP_MASK); + + /* carry the carry */ + u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + } + t.dp[ix + pb] = u; + } + mp_clamp(&t); + mp_exch(&t, c); + mp_clear(&t); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_mul_high_comba.c b/src/libtommath/s_mp_mul_high_comba.c new file mode 100644 index 000000000..317346dfa --- /dev/null +++ b/src/libtommath/s_mp_mul_high_comba.c @@ -0,0 +1,70 @@ +#include "tommath_private.h" +#ifdef S_MP_MUL_HIGH_COMBA_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* this is a modified version of s_mp_mul_comba that only produces + * output digits *above* digs. See the comments for s_mp_mul_comba + * 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. + */ +mp_err s_mp_mul_high_comba(const mp_int *a, const mp_int *b, mp_int *c, int digs) +{ + int oldused, pa, ix; + mp_err err; + mp_digit W[MP_WARRAY]; + mp_word _W; + + /* grow the destination as required */ + pa = a->used + b->used; + if ((err = mp_grow(c, pa)) != MP_OKAY) { + return err; + } + + /* number of output digits to produce */ + pa = a->used + b->used; + _W = 0; + for (ix = digs; ix < pa; ix++) { + int tx, ty, iy, iz; + + /* get offsets into the two bignums */ + ty = MP_MIN(b->used-1, ix); + tx = ix - ty; + + /* this is the number of times the loop will iterrate, essentially its + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MP_MIN(a->used-tx, ty+1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += (mp_word)a->dp[tx + iz] * (mp_word)b->dp[ty - iz]; + } + + /* store term */ + W[ix] = (mp_digit)_W & MP_MASK; + + /* make next carry */ + _W = _W >> (mp_word)MP_DIGIT_BIT; + } + + /* setup dest */ + oldused = c->used; + c->used = pa; + + for (ix = digs; ix < pa; ix++) { + /* now extract the previous digit [below the carry] */ + c->dp[ix] = W[ix]; + } + + /* clear unused digits [that existed in the old copy of c] */ + s_mp_zero_digs(c->dp + c->used, oldused - c->used); + + mp_clamp(c); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_mul_karatsuba.c b/src/libtommath/s_mp_mul_karatsuba.c new file mode 100644 index 000000000..bf9271f3b --- /dev/null +++ b/src/libtommath/s_mp_mul_karatsuba.c @@ -0,0 +1,151 @@ +#include "tommath_private.h" +#ifdef S_MP_MUL_KARATSUBA_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* c = |a| * |b| using Karatsuba Multiplication using + * three half size multiplications + * + * Let B represent the radix [e.g. 2**MP_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. + */ +mp_err s_mp_mul_karatsuba(const mp_int *a, const mp_int *b, mp_int *c) +{ + mp_int x0, x1, y0, y1, t1, x0y0, x1y1; + int B; + mp_err err; + + /* min # of digits */ + B = MP_MIN(a->used, b->used); + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if ((err = mp_init_size(&x0, B)) != MP_OKAY) { + goto LBL_ERR; + } + if ((err = mp_init_size(&x1, a->used - B)) != MP_OKAY) { + goto X0; + } + if ((err = mp_init_size(&y0, B)) != MP_OKAY) { + goto X1; + } + if ((err = mp_init_size(&y1, b->used - B)) != MP_OKAY) { + goto Y0; + } + + /* init temps */ + if ((err = mp_init_size(&t1, B * 2)) != MP_OKAY) { + goto Y1; + } + if ((err = mp_init_size(&x0y0, B * 2)) != MP_OKAY) { + goto T1; + } + if ((err = mp_init_size(&x1y1, B * 2)) != MP_OKAY) { + goto X0Y0; + } + + /* now shift the digits */ + x0.used = y0.used = B; + x1.used = a->used - B; + y1.used = b->used - B; + + /* we copy the digits directly instead of using higher level functions + * since we also need to shift the digits + */ + s_mp_copy_digs(x0.dp, a->dp, x0.used); + s_mp_copy_digs(y0.dp, b->dp, y0.used); + s_mp_copy_digs(x1.dp, a->dp + B, x1.used); + s_mp_copy_digs(y1.dp, b->dp + B, y1.used); + + /* 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 ((err = mp_mul(&x0, &y0, &x0y0)) != MP_OKAY) { + goto X1Y1; /* x0y0 = x0*y0 */ + } + if ((err = mp_mul(&x1, &y1, &x1y1)) != MP_OKAY) { + goto X1Y1; /* x1y1 = x1*y1 */ + } + + /* now calc x1+x0 and y1+y0 */ + if ((err = s_mp_add(&x1, &x0, &t1)) != MP_OKAY) { + goto X1Y1; /* t1 = x1 - x0 */ + } + if ((err = s_mp_add(&y1, &y0, &x0)) != MP_OKAY) { + goto X1Y1; /* t2 = y1 - y0 */ + } + if ((err = mp_mul(&t1, &x0, &t1)) != MP_OKAY) { + goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */ + } + + /* add x0y0 */ + if ((err = mp_add(&x0y0, &x1y1, &x0)) != MP_OKAY) { + goto X1Y1; /* t2 = x0y0 + x1y1 */ + } + if ((err = s_mp_sub(&t1, &x0, &t1)) != MP_OKAY) { + goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ + } + + /* shift by B */ + if ((err = mp_lshd(&t1, B)) != MP_OKAY) { + goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used, b->used) / 3; + + /** a = a2 * x^2 + a1 * x + a0; */ + if ((err = mp_init_size(&a0, B)) != MP_OKAY) goto LBL_ERRa0; + if ((err = mp_init_size(&a1, B)) != MP_OKAY) goto LBL_ERRa1; + if ((err = mp_init_size(&a2, a->used - 2 * B)) != MP_OKAY) goto LBL_ERRa2; + + a0.used = a1.used = B; + a2.used = a->used - 2 * B; + s_mp_copy_digs(a0.dp, a->dp, a0.used); + s_mp_copy_digs(a1.dp, a->dp + B, a1.used); + s_mp_copy_digs(a2.dp, a->dp + 2 * B, a2.used); + mp_clamp(&a0); + mp_clamp(&a1); + mp_clamp(&a2); + + /** b = b2 * x^2 + b1 * x + b0; */ + if ((err = mp_init_size(&b0, B)) != MP_OKAY) goto LBL_ERRb0; + if ((err = mp_init_size(&b1, B)) != MP_OKAY) goto LBL_ERRb1; + if ((err = mp_init_size(&b2, b->used - 2 * B)) != MP_OKAY) goto LBL_ERRb2; + + b0.used = b1.used = B; + b2.used = b->used - 2 * B; + s_mp_copy_digs(b0.dp, b->dp, b0.used); + s_mp_copy_digs(b1.dp, b->dp + B, b1.used); + s_mp_copy_digs(b2.dp, b->dp + 2 * B, b2.used); + mp_clamp(&b0); + mp_clamp(&b1); + mp_clamp(&b2); + + /** \\ S1 = (a2+a1+a0) * (b2+b1+b0); */ + /** T1 = a2 + a1; */ + if ((err = mp_add(&a2, &a1, &T1)) != MP_OKAY) goto LBL_ERR; + + /** S2 = T1 + a0; */ + if ((err = mp_add(&T1, &a0, &S2)) != MP_OKAY) goto LBL_ERR; + + /** c = b2 + b1; */ + if ((err = mp_add(&b2, &b1, c)) != MP_OKAY) goto LBL_ERR; + + /** S1 = c + b0; */ + if ((err = mp_add(c, &b0, &S1)) != MP_OKAY) goto LBL_ERR; + + /** S1 = S1 * S2; */ + if ((err = mp_mul(&S1, &S2, &S1)) != MP_OKAY) goto LBL_ERR; + + /** \\S2 = (4*a2+2*a1+a0) * (4*b2+2*b1+b0); */ + /** T1 = T1 + a2; */ + if ((err = mp_add(&T1, &a2, &T1)) != MP_OKAY) goto LBL_ERR; + + /** T1 = T1 << 1; */ + if ((err = mp_mul_2(&T1, &T1)) != MP_OKAY) goto LBL_ERR; + + /** T1 = T1 + a0; */ + if ((err = mp_add(&T1, &a0, &T1)) != MP_OKAY) goto LBL_ERR; + + /** c = c + b2; */ + if ((err = mp_add(c, &b2, c)) != MP_OKAY) goto LBL_ERR; + + /** c = c << 1; */ + if ((err = mp_mul_2(c, c)) != MP_OKAY) goto LBL_ERR; + + /** c = c + b0; */ + if ((err = mp_add(c, &b0, c)) != MP_OKAY) goto LBL_ERR; + + /** S2 = T1 * c; */ + if ((err = mp_mul(&T1, c, &S2)) != MP_OKAY) goto LBL_ERR; + + /** \\S3 = (a2-a1+a0) * (b2-b1+b0); */ + /** a1 = a2 - a1; */ + if ((err = mp_sub(&a2, &a1, &a1)) != MP_OKAY) goto LBL_ERR; + + /** a1 = a1 + a0; */ + if ((err = mp_add(&a1, &a0, &a1)) != MP_OKAY) goto LBL_ERR; + + /** b1 = b2 - b1; */ + if ((err = mp_sub(&b2, &b1, &b1)) != MP_OKAY) goto LBL_ERR; + + /** b1 = b1 + b0; */ + if ((err = mp_add(&b1, &b0, &b1)) != MP_OKAY) goto LBL_ERR; + + /** a1 = a1 * b1; */ + if ((err = mp_mul(&a1, &b1, &a1)) != MP_OKAY) goto LBL_ERR; + + /** b1 = a2 * b2; */ + if ((err = mp_mul(&a2, &b2, &b1)) != MP_OKAY) goto LBL_ERR; + + /** \\S2 = (S2 - S3)/3; */ + /** S2 = S2 - a1; */ + if ((err = mp_sub(&S2, &a1, &S2)) != MP_OKAY) goto LBL_ERR; + + /** S2 = S2 / 3; \\ this is an exact division */ + if ((err = s_mp_div_3(&S2, &S2, NULL)) != MP_OKAY) goto LBL_ERR; + + /** a1 = S1 - a1; */ + if ((err = mp_sub(&S1, &a1, &a1)) != MP_OKAY) goto LBL_ERR; + + /** a1 = a1 >> 1; */ + if ((err = mp_div_2(&a1, &a1)) != MP_OKAY) goto LBL_ERR; + + /** a0 = a0 * b0; */ + if ((err = mp_mul(&a0, &b0, &a0)) != MP_OKAY) goto LBL_ERR; + + /** S1 = S1 - a0; */ + if ((err = mp_sub(&S1, &a0, &S1)) != MP_OKAY) goto LBL_ERR; + + /** S2 = S2 - S1; */ + if ((err = mp_sub(&S2, &S1, &S2)) != MP_OKAY) goto LBL_ERR; + + /** S2 = S2 >> 1; */ + if ((err = mp_div_2(&S2, &S2)) != MP_OKAY) goto LBL_ERR; + + /** S1 = S1 - a1; */ + if ((err = mp_sub(&S1, &a1, &S1)) != MP_OKAY) goto LBL_ERR; + + /** S1 = S1 - b1; */ + if ((err = mp_sub(&S1, &b1, &S1)) != MP_OKAY) goto LBL_ERR; + + /** T1 = b1 << 1; */ + if ((err = mp_mul_2(&b1, &T1)) != MP_OKAY) goto LBL_ERR; + + /** S2 = S2 - T1; */ + if ((err = mp_sub(&S2, &T1, &S2)) != MP_OKAY) goto LBL_ERR; + + /** a1 = a1 - S2; */ + if ((err = mp_sub(&a1, &S2, &a1)) != MP_OKAY) goto LBL_ERR; + + + /** P = b1*x^4+ S2*x^3+ S1*x^2+ a1*x + a0; */ + if ((err = mp_lshd(&b1, 4 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_lshd(&S2, 3 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&b1, &S2, &b1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_lshd(&S1, 2 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&b1, &S1, &b1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_lshd(&a1, 1 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&b1, &a1, &b1)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&b1, &a0, c)) != MP_OKAY) goto LBL_ERR; + + /** a * b - P */ + + +LBL_ERR: + mp_clear(&b2); +LBL_ERRb2: + mp_clear(&b1); +LBL_ERRb1: + mp_clear(&b0); +LBL_ERRb0: + mp_clear(&a2); +LBL_ERRa2: + mp_clear(&a1); +LBL_ERRa1: + mp_clear(&a0); +LBL_ERRa0: + mp_clear_multi(&S1, &S2, &T1, NULL); + return err; +} + +#endif diff --git a/src/libtommath/s_mp_prime_is_divisible.c b/src/libtommath/s_mp_prime_is_divisible.c new file mode 100644 index 000000000..63b2405ab --- /dev/null +++ b/src/libtommath/s_mp_prime_is_divisible.c @@ -0,0 +1,33 @@ +#include "tommath_private.h" +#ifdef S_MP_PRIME_IS_DIVISIBLE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* 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 + */ +mp_err s_mp_prime_is_divisible(const mp_int *a, bool *result) +{ + int i; + for (i = 0; i < MP_PRIME_TAB_SIZE; i++) { + /* what is a mod LBL_prime_tab[i] */ + mp_err err; + mp_digit res; + if ((err = mp_mod_d(a, s_mp_prime_tab[i], &res)) != MP_OKAY) { + return err; + } + + /* is the residue zero? */ + if (res == 0u) { + *result = true; + return MP_OKAY; + } + } + + /* default to not */ + *result = false; + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_prime_tab.c b/src/libtommath/s_mp_prime_tab.c new file mode 100644 index 000000000..87c07fd95 --- /dev/null +++ b/src/libtommath/s_mp_prime_tab.c @@ -0,0 +1,44 @@ +#include "tommath_private.h" +#ifdef S_MP_PRIME_TAB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +const mp_digit s_mp_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, 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/src/libtommath/s_mp_radix_map.c b/src/libtommath/s_mp_radix_map.c new file mode 100644 index 000000000..68e21f32e --- /dev/null +++ b/src/libtommath/s_mp_radix_map.c @@ -0,0 +1,19 @@ +#include "tommath_private.h" +#ifdef S_MP_RADIX_MAP_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* chars used in radix conversions */ +const char s_mp_radix_map[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; +const uint8_t s_mp_radix_map_reverse[] = { + 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */ + 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */ + 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */ + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* IJKLMNOPQR */ + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, /* STUVWXYZ[\ */ + 0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */ + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */ + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d /* qrstuvwxyz */ +}; +MP_STATIC_ASSERT(correct_radix_map_reverse_size, sizeof(s_mp_radix_map_reverse) == MP_RADIX_MAP_REVERSE_SIZE) +#endif diff --git a/src/libtommath/s_mp_radix_size_overestimate.c b/src/libtommath/s_mp_radix_size_overestimate.c new file mode 100644 index 000000000..4f0599732 --- /dev/null +++ b/src/libtommath/s_mp_radix_size_overestimate.c @@ -0,0 +1,82 @@ +#include "tommath_private.h" +#ifdef S_MP_RADIX_SIZE_OVERESTIMATE_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* + Overestimate the size needed for the bigint to string conversion by a very small amount. + The error is about 10^-8; it will overestimate the result by at most 11 elements for + a number of the size 2^(2^31)-1 which is currently the largest possible in this library. + Some short tests gave no results larger than 5 (plus 2 for sign and EOS). + */ + +/* + Table of {0, INT(log_2([1..64])*2^p)+1 } where p is the scale + factor defined in MP_RADIX_SIZE_SCALE and INT() extracts the integer part (truncating). + Good for 32 bit "int". Set MP_RADIX_SIZE_SCALE = 61 and recompute values + for 64 bit "int". + */ +/* *INDENT-OFF* */ +#define MP_RADIX_SIZE_SCALE 29 +static const uint32_t s_log_bases[65] = { + 0u, 0u, 0x20000001u, 0x14309399u, 0x10000001u, + 0xdc81a35u, 0xc611924u, 0xb660c9eu, 0xaaaaaabu, 0xa1849cdu, + 0x9a209a9u, 0x94004e1u, 0x8ed19c2u, 0x8a5ca7du, 0x867a000u, + 0x830cee3u, 0x8000001u, 0x7d42d60u, 0x7ac8b32u, 0x7887847u, + 0x7677349u, 0x749131fu, 0x72d0163u, 0x712f657u, 0x6fab5dbu, + 0x6e40d1bu, 0x6ced0d0u, 0x6badbdeu, 0x6a80e3bu, 0x6964c19u, + 0x6857d31u, 0x6758c38u, 0x6666667u, 0x657fb21u, 0x64a3b9fu, + 0x63d1ab4u, 0x6308c92u, 0x624869eu, 0x618ff47u, 0x60dedeau, + 0x6034ab0u, 0x5f90e7bu, 0x5ef32cbu, 0x5e5b1b2u, 0x5dc85c3u, + 0x5d3aa02u, 0x5cb19d9u, 0x5c2d10fu, 0x5bacbbfu, 0x5b3064fu, + 0x5ab7d68u, 0x5a42df0u, 0x59d1506u, 0x5962ffeu, 0x58f7c57u, + 0x588f7bcu, 0x582a000u, 0x57c7319u, 0x5766f1du, 0x5709243u, + 0x56adad9u, 0x565474du, 0x55fd61fu, 0x55a85e8u, 0x5555556u +}; +/* *INDENT-ON* */ + +mp_err s_mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size) +{ + int bit_count; + mp_int bi_bit_count, bi_k; + mp_err err = MP_OKAY; + + if ((radix < 2) || (radix > 64)) { + return MP_VAL; + } + + if (mp_iszero(a)) { + *size = 2U; + return MP_OKAY; + } + + if (MP_HAS(S_MP_LOG_2EXPT) && MP_IS_2EXPT((mp_digit)radix)) { + /* floor(log_{2^n}(a)) + 1 + EOS + sign */ + *size = (size_t)(s_mp_log_2expt(a, (mp_digit)radix) + 3); + return MP_OKAY; + } + + if ((err = mp_init_multi(&bi_bit_count, &bi_k, NULL)) != MP_OKAY) { + return err; + } + + /* la = floor(log_2(a)) + 1 */ + bit_count = mp_count_bits(a); + + mp_set_u32(&bi_bit_count, (uint32_t)bit_count); + /* k = floor(2^29/log_2(radix)) + 1 */ + mp_set_u32(&bi_k, s_log_bases[radix]); + /* n = floor((la * k) / 2^29) + 1 */ + if ((err = mp_mul(&bi_bit_count, &bi_k, &bi_bit_count)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_div_2d(&bi_bit_count, MP_RADIX_SIZE_SCALE, &bi_bit_count, NULL)) != MP_OKAY) goto LBL_ERR; + + /* The "+1" here is the "+1" in "floor((la * k) / 2^29) + 1" */ + /* n = n + 1 + EOS + sign */ + *size = (size_t)(mp_get_u64(&bi_bit_count) + 3U); + +LBL_ERR: + mp_clear_multi(&bi_bit_count, &bi_k, NULL); + return err; +} + +#endif diff --git a/src/libtommath/s_mp_rand_platform.c b/src/libtommath/s_mp_rand_platform.c new file mode 100644 index 000000000..06b2f1bda --- /dev/null +++ b/src/libtommath/s_mp_rand_platform.c @@ -0,0 +1,149 @@ +#include "tommath_private.h" +#ifdef S_MP_RAND_PLATFORM_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* First the OS-specific special cases + * - *BSD + * - Windows + */ +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#define S_READ_ARC4RANDOM_C +static mp_err s_read_arc4random(void *p, size_t n) +{ + arc4random_buf(p, n); + return MP_OKAY; +} +#endif + +#if defined(_WIN32) +#define S_READ_WINCSP_C + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif +#ifndef WINVER +#define WINVER 0x0501 +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#include + +static mp_err s_read_wincsp(void *p, size_t n) +{ + static HCRYPTPROV hProv = 0; + if (hProv == 0) { + HCRYPTPROV h = 0; + if (!CryptAcquireContextW(&h, NULL, MS_DEF_PROV_W, PROV_RSA_FULL, + (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) && + !CryptAcquireContextW(&h, NULL, MS_DEF_PROV_W, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) { + return MP_ERR; + } + hProv = h; + } + return CryptGenRandom(hProv, (DWORD)n, (BYTE *)p) == TRUE ? MP_OKAY : MP_ERR; +} +#endif /* WIN32 */ + +#if !defined(S_READ_WINCSP_C) && defined(__linux__) && defined(__GLIBC_PREREQ) +#if __GLIBC_PREREQ(2, 25) +#define S_READ_GETRANDOM_C +#include +#include + +static mp_err s_read_getrandom(void *p, size_t n) +{ + char *q = (char *)p; + while (n > 0u) { + ssize_t ret = getrandom(q, n, 0); + if (ret < 0) { + if (errno == EINTR) { + continue; + } + return MP_ERR; + } + q += ret; + n -= (size_t)ret; + } + return MP_OKAY; +} +#endif +#endif + +/* We assume all platforms besides windows provide "/dev/urandom". + * In case yours doesn't, define MP_NO_DEV_URANDOM at compile-time. + */ +#if !defined(S_READ_WINCSP_C) && !defined(MP_NO_DEV_URANDOM) +#define S_READ_URANDOM_C +#ifndef MP_DEV_URANDOM +#define MP_DEV_URANDOM "/dev/urandom" +#endif +#include +#include +#include + +static mp_err s_read_urandom(void *p, size_t n) +{ + int fd; + char *q = (char *)p; + + do { + fd = open(MP_DEV_URANDOM, O_RDONLY); + } while ((fd == -1) && (errno == EINTR)); + if (fd == -1) return MP_ERR; + + while (n > 0u) { + ssize_t ret = read(fd, p, n); + if (ret < 0) { + if (errno == EINTR) { + continue; + } + close(fd); + return MP_ERR; + } + q += ret; + n -= (size_t)ret; + } + + close(fd); + return MP_OKAY; +} +#endif + +mp_err s_read_arc4random(void *p, size_t n); +mp_err s_read_wincsp(void *p, size_t n); +mp_err s_read_getrandom(void *p, size_t n); +mp_err s_read_urandom(void *p, size_t n); + +/* + * Note: libtommath relies on dead code elimination + * for the configuration system, i.e., the MP_HAS macro. + * + * If you observe linking errors in this functions, + * your compiler does not perform the dead code compilation + * such that the unused functions are still referenced. + * + * This happens for example for MSVC if the /Od compilation + * option is given. The option /Od instructs MSVC to + * not perform any "optimizations", not even removal of + * dead code wrapped in `if (0)` blocks. + * + * If you still insist on compiling with /Od, simply + * comment out the lines which result in linking errors. + * + * We intentionally don't fix this issue in order + * to have a single point of failure for misconfigured compilers. + */ +mp_err s_mp_rand_platform(void *p, size_t n) +{ + mp_err err = MP_ERR; + if ((err != MP_OKAY) && MP_HAS(S_READ_ARC4RANDOM)) err = s_read_arc4random(p, n); + if ((err != MP_OKAY) && MP_HAS(S_READ_WINCSP)) err = s_read_wincsp(p, n); + if ((err != MP_OKAY) && MP_HAS(S_READ_GETRANDOM)) err = s_read_getrandom(p, n); + if ((err != MP_OKAY) && MP_HAS(S_READ_URANDOM)) err = s_read_urandom(p, n); + return err; +} + +#endif diff --git a/src/libtommath/s_mp_sqr.c b/src/libtommath/s_mp_sqr.c new file mode 100644 index 000000000..4a2030638 --- /dev/null +++ b/src/libtommath/s_mp_sqr.c @@ -0,0 +1,65 @@ +#include "tommath_private.h" +#ifdef S_MP_SQR_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ +mp_err s_mp_sqr(const mp_int *a, mp_int *b) +{ + mp_int t; + int ix, pa; + mp_err err; + + pa = a->used; + if ((err = mp_init_size(&t, (2 * pa) + 1)) != MP_OKAY) { + return err; + } + + /* default used is maximum possible size */ + t.used = (2 * pa) + 1; + + for (ix = 0; ix < pa; ix++) { + mp_digit u; + int iy; + + /* first calculate the digit at 2*ix */ + /* calculate double precision result */ + mp_word 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)MP_DIGIT_BIT); + + for (iy = ix + 1; iy < pa; iy++) { + /* first calculate the product */ + r = (mp_word)a->dp[ix] * (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)t.dp[ix + iy] + r + r + (mp_word)u; + + /* store lower part */ + t.dp[ix + iy] = (mp_digit)(r & (mp_word)MP_MASK); + + /* get carry */ + u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + } + /* propagate upwards */ + while (u != 0uL) { + r = (mp_word)t.dp[ix + iy] + (mp_word)u; + t.dp[ix + iy] = (mp_digit)(r & (mp_word)MP_MASK); + u = (mp_digit)(r >> (mp_word)MP_DIGIT_BIT); + ++iy; + } + } + + mp_clamp(&t); + mp_exch(&t, b); + mp_clear(&t); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_sqr_comba.c b/src/libtommath/s_mp_sqr_comba.c new file mode 100644 index 000000000..cb88dcc9e --- /dev/null +++ b/src/libtommath/s_mp_sqr_comba.c @@ -0,0 +1,87 @@ +#include "tommath_private.h" +#ifdef S_MP_SQR_COMBA_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* the jist of squaring... + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. + * So basically you set up iy like before then you min it with + * (ty-tx) so that it never happens. You double all those + * you add in the inner loop + +After that loop you do the squares and add them in. +*/ + +mp_err s_mp_sqr_comba(const mp_int *a, mp_int *b) +{ + int oldused, pa, ix; + mp_digit W[MP_WARRAY]; + mp_word W1; + mp_err err; + + /* grow the destination as required */ + pa = a->used + a->used; + if ((err = mp_grow(b, pa)) != MP_OKAY) { + return err; + } + + /* number of output digits to produce */ + W1 = 0; + for (ix = 0; ix < pa; ix++) { + int tx, ty, iy, iz; + mp_word _W; + + /* clear counter */ + _W = 0; + + /* get offsets into the two bignums */ + ty = MP_MIN(a->used-1, ix); + tx = ix - ty; + + /* this is the number of times the loop will iterrate, essentially + while (tx++ < a->used && ty-- >= 0) { ... } + */ + iy = MP_MIN(a->used-tx, ty+1); + + /* now for squaring tx can never equal ty + * we halve the distance since they approach at a rate of 2x + * and we have to round because odd cases need to be executed + */ + iy = MP_MIN(iy, ((ty-tx)+1)>>1); + + /* execute loop */ + for (iz = 0; iz < iy; iz++) { + _W += (mp_word)a->dp[tx + iz] * (mp_word)a->dp[ty - iz]; + } + + /* double the inner product and add carry */ + _W = _W + _W + W1; + + /* even columns have the square term in them */ + if (((unsigned)ix & 1u) == 0u) { + _W += (mp_word)a->dp[ix>>1] * (mp_word)a->dp[ix>>1]; + } + + /* store it */ + W[ix] = (mp_digit)_W & MP_MASK; + + /* make next carry */ + W1 = _W >> (mp_word)MP_DIGIT_BIT; + } + + /* setup dest */ + oldused = b->used; + b->used = a->used+a->used; + + for (ix = 0; ix < pa; ix++) { + b->dp[ix] = W[ix] & MP_MASK; + } + + /* clear unused digits [that existed in the old copy of c] */ + s_mp_zero_digs(b->dp + b->used, oldused - b->used); + + mp_clamp(b); + return MP_OKAY; +} +#endif diff --git a/src/libtommath/s_mp_sqr_karatsuba.c b/src/libtommath/s_mp_sqr_karatsuba.c new file mode 100644 index 000000000..f064b46a1 --- /dev/null +++ b/src/libtommath/s_mp_sqr_karatsuba.c @@ -0,0 +1,92 @@ +#include "tommath_private.h" +#ifdef S_MP_SQR_KARATSUBA_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* Karatsuba squaring, computes b = a*a using three + * half size squarings + * + * See comments of mul_karatsuba for details. It + * is essentially the same algorithm but merely + * tuned to perform recursive squarings. + */ +mp_err s_mp_sqr_karatsuba(const mp_int *a, mp_int *b) +{ + mp_int x0, x1, t1, t2, x0x0, x1x1; + int B; + mp_err err; + + /* min # of digits */ + B = a->used; + + /* now divide in two */ + B = B >> 1; + + /* init copy all the temps */ + if ((err = mp_init_size(&x0, B)) != MP_OKAY) + goto LBL_ERR; + if ((err = mp_init_size(&x1, a->used - B)) != MP_OKAY) + goto X0; + + /* init temps */ + if ((err = mp_init_size(&t1, a->used * 2)) != MP_OKAY) + goto X1; + if ((err = mp_init_size(&t2, a->used * 2)) != MP_OKAY) + goto T1; + if ((err = mp_init_size(&x0x0, B * 2)) != MP_OKAY) + goto T2; + if ((err = mp_init_size(&x1x1, (a->used - B) * 2)) != MP_OKAY) + goto X0X0; + + /* now shift the digits */ + x0.used = B; + x1.used = a->used - B; + s_mp_copy_digs(x0.dp, a->dp, x0.used); + s_mp_copy_digs(x1.dp, a->dp + B, x1.used); + mp_clamp(&x0); + + /* now calc the products x0*x0 and x1*x1 */ + if ((err = mp_sqr(&x0, &x0x0)) != MP_OKAY) + goto X1X1; /* x0x0 = x0*x0 */ + if ((err = mp_sqr(&x1, &x1x1)) != MP_OKAY) + goto X1X1; /* x1x1 = x1*x1 */ + + /* now calc (x1+x0)**2 */ + if ((err = s_mp_add(&x1, &x0, &t1)) != MP_OKAY) + goto X1X1; /* t1 = x1 - x0 */ + if ((err = mp_sqr(&t1, &t1)) != MP_OKAY) + goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */ + + /* add x0y0 */ + if ((err = s_mp_add(&x0x0, &x1x1, &t2)) != MP_OKAY) + goto X1X1; /* t2 = x0x0 + x1x1 */ + if ((err = s_mp_sub(&t1, &t2, &t1)) != MP_OKAY) + goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ + + /* shift by B */ + if ((err = mp_lshd(&t1, B)) != MP_OKAY) + goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<used / 3; + + /** a = a2 * x^2 + a1 * x + a0; */ + if ((err = mp_init_size(&a0, B)) != MP_OKAY) goto LBL_ERRa0; + if ((err = mp_init_size(&a1, B)) != MP_OKAY) goto LBL_ERRa1; + if ((err = mp_init_size(&a2, a->used - (2 * B))) != MP_OKAY) goto LBL_ERRa2; + + a0.used = a1.used = B; + a2.used = a->used - 2 * B; + s_mp_copy_digs(a0.dp, a->dp, a0.used); + s_mp_copy_digs(a1.dp, a->dp + B, a1.used); + s_mp_copy_digs(a2.dp, a->dp + 2 * B, a2.used); + mp_clamp(&a0); + mp_clamp(&a1); + mp_clamp(&a2); + + /** S0 = a0^2; */ + if ((err = mp_sqr(&a0, &S0)) != MP_OKAY) goto LBL_ERR; + + /** \\S1 = (a2 + a1 + a0)^2 */ + /** \\S2 = (a2 - a1 + a0)^2 */ + /** \\S1 = a0 + a2; */ + /** a0 = a0 + a2; */ + if ((err = mp_add(&a0, &a2, &a0)) != MP_OKAY) goto LBL_ERR; + /** \\S2 = S1 - a1; */ + /** b = a0 - a1; */ + if ((err = mp_sub(&a0, &a1, b)) != MP_OKAY) goto LBL_ERR; + /** \\S1 = S1 + a1; */ + /** a0 = a0 + a1; */ + if ((err = mp_add(&a0, &a1, &a0)) != MP_OKAY) goto LBL_ERR; + /** \\S1 = S1^2; */ + /** a0 = a0^2; */ + if ((err = mp_sqr(&a0, &a0)) != MP_OKAY) goto LBL_ERR; + /** \\S2 = S2^2; */ + /** b = b^2; */ + if ((err = mp_sqr(b, b)) != MP_OKAY) goto LBL_ERR; + + /** \\ S3 = 2 * a1 * a2 */ + /** \\S3 = a1 * a2; */ + /** a1 = a1 * a2; */ + if ((err = mp_mul(&a1, &a2, &a1)) != MP_OKAY) goto LBL_ERR; + /** \\S3 = S3 << 1; */ + /** a1 = a1 << 1; */ + if ((err = mp_mul_2(&a1, &a1)) != MP_OKAY) goto LBL_ERR; + + /** \\S4 = a2^2; */ + /** a2 = a2^2; */ + if ((err = mp_sqr(&a2, &a2)) != MP_OKAY) goto LBL_ERR; + + /** \\ tmp = (S1 + S2)/2 */ + /** \\tmp = S1 + S2; */ + /** b = a0 + b; */ + if ((err = mp_add(&a0, b, b)) != MP_OKAY) goto LBL_ERR; + /** \\tmp = tmp >> 1; */ + /** b = b >> 1; */ + if ((err = mp_div_2(b, b)) != MP_OKAY) goto LBL_ERR; + + /** \\ S1 = S1 - tmp - S3 */ + /** \\S1 = S1 - tmp; */ + /** a0 = a0 - b; */ + if ((err = mp_sub(&a0, b, &a0)) != MP_OKAY) goto LBL_ERR; + /** \\S1 = S1 - S3; */ + /** a0 = a0 - a1; */ + if ((err = mp_sub(&a0, &a1, &a0)) != MP_OKAY) goto LBL_ERR; + + /** \\S2 = tmp - S4 -S0 */ + /** \\S2 = tmp - S4; */ + /** b = b - a2; */ + if ((err = mp_sub(b, &a2, b)) != MP_OKAY) goto LBL_ERR; + /** \\S2 = S2 - S0; */ + /** b = b - S0; */ + if ((err = mp_sub(b, &S0, b)) != MP_OKAY) goto LBL_ERR; + + + /** \\P = S4*x^4 + S3*x^3 + S2*x^2 + S1*x + S0; */ + /** P = a2*x^4 + a1*x^3 + b*x^2 + a0*x + S0; */ + + if ((err = mp_lshd(&a2, 4 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_lshd(&a1, 3 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_lshd(b, 2 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_lshd(&a0, 1 * B)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&a2, &a1, &a2)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(&a2, b, b)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(b, &a0, b)) != MP_OKAY) goto LBL_ERR; + if ((err = mp_add(b, &S0, b)) != MP_OKAY) goto LBL_ERR; + /** a^2 - P */ + + +LBL_ERR: + mp_clear(&a2); +LBL_ERRa2: + mp_clear(&a1); +LBL_ERRa1: + mp_clear(&a0); +LBL_ERRa0: + mp_clear(&S0); + + return err; +} + +#endif diff --git a/src/libtommath/s_mp_sub.c b/src/libtommath/s_mp_sub.c new file mode 100644 index 000000000..b1a749e35 --- /dev/null +++ b/src/libtommath/s_mp_sub.c @@ -0,0 +1,56 @@ +#include "tommath_private.h" +#ifdef S_MP_SUB_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ +mp_err s_mp_sub(const mp_int *a, const mp_int *b, mp_int *c) +{ + int oldused = c->used, min = b->used, max = a->used, i; + mp_digit u; + mp_err err; + + /* init result */ + if ((err = mp_grow(c, max)) != MP_OKAY) { + return err; + } + + c->used = max; + + /* set carry to zero */ + u = 0; + for (i = 0; i < min; i++) { + /* T[i] = A[i] - B[i] - U */ + c->dp[i] = (a->dp[i] - b->dp[i]) - u; + + /* U = carry bit of T[i] + * 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 = c->dp[i] >> (MP_SIZEOF_BITS(mp_digit) - 1u); + + /* Clear carry from T[i] */ + c->dp[i] &= MP_MASK; + } + + /* now copy higher words if any, e.g. if A has more digits than B */ + for (; i < max; i++) { + /* T[i] = A[i] - U */ + c->dp[i] = a->dp[i] - u; + + /* U = carry bit of T[i] */ + u = c->dp[i] >> (MP_SIZEOF_BITS(mp_digit) - 1u); + + /* Clear carry from T[i] */ + c->dp[i] &= MP_MASK; + } + + /* clear digits above used (since we may not have grown result above) */ + s_mp_zero_digs(c->dp + c->used, oldused - c->used); + + mp_clamp(c); + return MP_OKAY; +} + +#endif diff --git a/src/libtommath/s_mp_zero_buf.c b/src/libtommath/s_mp_zero_buf.c new file mode 100644 index 000000000..23a458dcd --- /dev/null +++ b/src/libtommath/s_mp_zero_buf.c @@ -0,0 +1,22 @@ +#include "tommath_private.h" +#ifdef S_MP_ZERO_BUF_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifdef MP_USE_MEMOPS +# include +#endif + +void s_mp_zero_buf(void *mem, size_t size) +{ +#ifdef MP_USE_MEMOPS + memset(mem, 0, size); +#else + char *m = (char *)mem; + while (size-- > 0u) { + *m++ = '\0'; + } +#endif +} + +#endif diff --git a/src/libtommath/s_mp_zero_digs.c b/src/libtommath/s_mp_zero_digs.c new file mode 100644 index 000000000..79e837796 --- /dev/null +++ b/src/libtommath/s_mp_zero_digs.c @@ -0,0 +1,23 @@ +#include "tommath_private.h" +#ifdef S_MP_ZERO_DIGS_C +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifdef MP_USE_MEMOPS +# include +#endif + +void s_mp_zero_digs(mp_digit *d, int digits) +{ +#ifdef MP_USE_MEMOPS + if (digits > 0) { + memset(d, 0, (size_t)digits * sizeof(mp_digit)); + } +#else + while (digits-- > 0) { + *d++ = 0; + } +#endif +} + +#endif diff --git a/src/libtommath/tommath.h b/src/libtommath/tommath.h new file mode 100644 index 000000000..ae633d1e1 --- /dev/null +++ b/src/libtommath/tommath.h @@ -0,0 +1,587 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifndef TOMMATH_H_ +#define TOMMATH_H_ + +#include +#include +#include + +#ifndef MP_NO_FILE +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* MS Visual C++ doesn't have a 128bit type for words, so fall back to 32bit MPI's (where words are 64bit) */ +#if (defined(_MSC_VER) || defined(__LLP64__) || defined(__e2k__) || defined(__LCC__)) && !defined(MP_64BIT) +# define MP_32BIT +#endif + +/* detect 64-bit mode if possible */ +#if defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) || \ + defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || \ + defined(__s390x__) || defined(__arch64__) || defined(__aarch64__) || \ + defined(__sparcv9) || defined(__sparc_v9__) || defined(__sparc64__) || \ + defined(__ia64) || defined(__ia64__) || defined(__itanium__) || defined(_M_IA64) || \ + defined(__LP64__) || defined(_LP64) || defined(__64BIT__) +# if !(defined(MP_64BIT) || defined(MP_32BIT) || defined(MP_16BIT)) +# if defined(__GNUC__) && !defined(__hppa) +/* we support 128bit integers only via: __attribute__((mode(TI))) */ +# define MP_64BIT +# else +/* otherwise we fall back to MP_32BIT even on 64bit platforms */ +# define MP_32BIT +# endif +# endif +#endif + +#ifdef MP_DIGIT_BIT +# error Defining MP_DIGIT_BIT is disallowed, use MP_16/31/32/64BIT +#endif + +/* some default configurations. + * + * A "mp_digit" must be able to hold MP_DIGIT_BIT + 1 bits + * A "mp_word" must be able to hold 2*MP_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] + */ + +#if defined(MP_16BIT) +typedef uint16_t mp_digit; +# define MP_DIGIT_BIT 15 +#elif defined(MP_64BIT) +typedef uint64_t mp_digit; +# define MP_DIGIT_BIT 60 +#else +typedef uint32_t mp_digit; +# ifdef MP_31BIT +/* + * This is an extension that uses 31-bit digits. + * Please be aware that not all functions support this size, especially s_mp_mul_comba + * will be reduced to work on small numbers only: + * Up to 8 limbs, 248 bits instead of up to 512 limbs, 15872 bits with MP_28BIT. + */ +# define MP_DIGIT_BIT 31 +# else +/* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ +# define MP_DIGIT_BIT 28 +# define MP_28BIT +# endif +#endif + +#define MP_MASK ((((mp_digit)1)<<((mp_digit)MP_DIGIT_BIT))-((mp_digit)1)) +#define MP_DIGIT_MAX MP_MASK + +/* Primality generation flags */ +#define MP_PRIME_BBS 0x0001 /* BBS style prime */ +#define MP_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ +#define MP_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ + +typedef enum { + MP_ZPOS = 0, /* positive */ + MP_NEG = 1 /* negative */ +} mp_sign; + +typedef enum { + MP_LT = -1, /* less than */ + MP_EQ = 0, /* equal */ + MP_GT = 1 /* greater than */ +} mp_ord; + +typedef enum { + MP_OKAY = 0, /* no error */ + MP_ERR = -1, /* unknown error */ + MP_MEM = -2, /* out of mem */ + MP_VAL = -3, /* invalid input */ + MP_ITER = -4, /* maximum iterations reached */ + MP_BUF = -5, /* buffer overflow, supplied buffer too small */ + MP_OVF = -6 /* mp_int overflow, too many digits */ +} mp_err; + +typedef enum { + MP_LSB_FIRST = -1, + MP_MSB_FIRST = 1 +} mp_order; + +typedef enum { + MP_LITTLE_ENDIAN = -1, + MP_NATIVE_ENDIAN = 0, + MP_BIG_ENDIAN = 1 +} mp_endian; + +/* tunable cutoffs */ +#ifndef MP_FIXED_CUTOFFS +extern int +MP_MUL_KARATSUBA_CUTOFF, +MP_SQR_KARATSUBA_CUTOFF, +MP_MUL_TOOM_CUTOFF, +MP_SQR_TOOM_CUTOFF; +#endif + +/* define this to use lower memory usage routines (exptmods mostly) */ +/* #define MP_LOW_MEM */ + +#if defined(__GNUC__) && __GNUC__ >= 4 +# define MP_NULL_TERMINATED __attribute__((sentinel)) +#else +# define MP_NULL_TERMINATED +#endif + +/* + * MP_WUR - warn unused result + * --------------------------- + * + * The result of functions annotated with MP_WUR must be + * checked and cannot be ignored. + * + * Most functions in libtommath return an error code. + * This error code must be checked in order to prevent crashes or invalid + * results. + */ +#if defined(__GNUC__) && __GNUC__ >= 4 +# define MP_WUR __attribute__((warn_unused_result)) +#else +# define MP_WUR +#endif + +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 405) +# define MP_DEPRECATED(x) __attribute__((deprecated("replaced by " #x))) +#elif defined(_MSC_VER) && _MSC_VER >= 1500 +# define MP_DEPRECATED(x) __declspec(deprecated("replaced by " #x)) +#else +# define MP_DEPRECATED(x) +#endif + +#ifndef MP_NO_DEPRECATED_PRAGMA +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 301) +# define PRIVATE_MP_DEPRECATED_PRAGMA(s) _Pragma(#s) +# define MP_DEPRECATED_PRAGMA(s) PRIVATE_MP_DEPRECATED_PRAGMA(GCC warning s) +#elif defined(_MSC_VER) && _MSC_VER >= 1500 +# define MP_DEPRECATED_PRAGMA(s) __pragma(message(s)) +#endif +#endif + +#ifndef MP_DEPRECATED_PRAGMA +# define MP_DEPRECATED_PRAGMA(s) +#endif + +/* the infamous mp_int structure */ +typedef struct { + int used, alloc; + mp_sign sign; + mp_digit *dp; +} mp_int; + +/* error code to char* string */ +const char *mp_error_to_string(mp_err code) MP_WUR; + +/* ---> init and deinit bignum functions <--- */ +/* init a bignum */ +mp_err mp_init(mp_int *a) MP_WUR; + +/* free a bignum */ +void mp_clear(mp_int *a); + +/* init a null terminated series of arguments */ +mp_err mp_init_multi(mp_int *mp, ...) MP_NULL_TERMINATED MP_WUR; + +/* clear a null terminated series of arguments */ +void mp_clear_multi(mp_int *mp, ...) MP_NULL_TERMINATED; + +/* exchange two ints */ +void mp_exch(mp_int *a, mp_int *b); + +/* shrink ram required for a bignum */ +mp_err mp_shrink(mp_int *a) MP_WUR; + +/* grow an int to a given size */ +mp_err mp_grow(mp_int *a, int size) MP_WUR; + +/* init to a given number of digits */ +mp_err mp_init_size(mp_int *a, int size) MP_WUR; + +/* ---> Basic Manipulations <--- */ +#define mp_iszero(a) ((a)->used == 0) +#define mp_isneg(a) ((a)->sign == MP_NEG) +#define mp_iseven(a) (((a)->used == 0) || (((a)->dp[0] & 1u) == 0u)) +#define mp_isodd(a) (!mp_iseven(a)) + +/* set to zero */ +void mp_zero(mp_int *a); + +/* get and set doubles */ +double mp_get_double(const mp_int *a) MP_WUR; +mp_err mp_set_double(mp_int *a, double b) MP_WUR; + +/* get integer, set integer and init with integer (int32_t) */ +int32_t mp_get_i32(const mp_int *a) MP_WUR; +void mp_set_i32(mp_int *a, int32_t b); +mp_err mp_init_i32(mp_int *a, int32_t b) MP_WUR; + +/* get integer, set integer and init with integer, behaves like two complement for negative numbers (uint32_t) */ +#define mp_get_u32(a) ((uint32_t)mp_get_i32(a)) +void mp_set_u32(mp_int *a, uint32_t b); +mp_err mp_init_u32(mp_int *a, uint32_t b) MP_WUR; + +/* get integer, set integer and init with integer (int64_t) */ +int64_t mp_get_i64(const mp_int *a) MP_WUR; +void mp_set_i64(mp_int *a, int64_t b); +mp_err mp_init_i64(mp_int *a, int64_t b) MP_WUR; + +/* get integer, set integer and init with integer, behaves like two complement for negative numbers (uint64_t) */ +#define mp_get_u64(a) ((uint64_t)mp_get_i64(a)) +void mp_set_u64(mp_int *a, uint64_t b); +mp_err mp_init_u64(mp_int *a, uint64_t b) MP_WUR; + +/* get magnitude */ +uint32_t mp_get_mag_u32(const mp_int *a) MP_WUR; +uint64_t mp_get_mag_u64(const mp_int *a) MP_WUR; +unsigned long mp_get_mag_ul(const mp_int *a) MP_WUR; + +/* get integer, set integer (long) */ +long mp_get_l(const mp_int *a) MP_WUR; +void mp_set_l(mp_int *a, long b); +mp_err mp_init_l(mp_int *a, long b) MP_WUR; + +/* get integer, set integer (unsigned long) */ +#define mp_get_ul(a) ((unsigned long)mp_get_l(a)) +void mp_set_ul(mp_int *a, unsigned long b); +mp_err mp_init_ul(mp_int *a, unsigned long b) MP_WUR; + +/* set to single unsigned digit, up to MP_DIGIT_MAX */ +void mp_set(mp_int *a, mp_digit b); +mp_err mp_init_set(mp_int *a, mp_digit b) MP_WUR; + +/* copy, b = a */ +mp_err mp_copy(const mp_int *a, mp_int *b) MP_WUR; + +/* inits and copies, a = b */ +mp_err mp_init_copy(mp_int *a, const mp_int *b) MP_WUR; + +/* trim unused digits */ +void mp_clamp(mp_int *a); + +/* unpack binary data */ +mp_err mp_unpack(mp_int *rop, size_t count, mp_order order, size_t size, mp_endian endian, + size_t nails, const void *op) MP_WUR; + +/* pack binary data */ +size_t mp_pack_count(const mp_int *a, size_t nails, size_t size) MP_WUR; +mp_err mp_pack(void *rop, size_t maxcount, size_t *written, mp_order order, size_t size, + mp_endian endian, size_t nails, const mp_int *op) MP_WUR; + +/* ---> digit manipulation <--- */ + +/* right shift by "b" digits */ +void mp_rshd(mp_int *a, int b); + +/* left shift by "b" digits */ +mp_err mp_lshd(mp_int *a, int b) MP_WUR; + +/* c = a / 2**b, implemented as c = a >> b */ +mp_err mp_div_2d(const mp_int *a, int b, mp_int *c, mp_int *d) MP_WUR; + +/* b = a/2 */ +mp_err mp_div_2(const mp_int *a, mp_int *b) MP_WUR; + +/* c = a * 2**b, implemented as c = a << b */ +mp_err mp_mul_2d(const mp_int *a, int b, mp_int *c) MP_WUR; + +/* b = a*2 */ +mp_err mp_mul_2(const mp_int *a, mp_int *b) MP_WUR; + +/* c = a mod 2**b */ +mp_err mp_mod_2d(const mp_int *a, int b, mp_int *c) MP_WUR; + +/* computes a = 2**b */ +mp_err mp_2expt(mp_int *a, int b) MP_WUR; + +/* Counts the number of lsbs which are zero before the first zero bit */ +int mp_cnt_lsb(const mp_int *a) MP_WUR; + +/* I Love Earth! */ + +/* makes a pseudo-random mp_int of a given size */ +// mp_err mp_rand(mp_int *a, int digits) MP_WUR; +/* use custom random data source instead of source provided the platform */ +// void mp_rand_source(mp_err(*source)(void *out, size_t size)); + +/* ---> binary operations <--- */ + +/* c = a XOR b (two complement) */ +mp_err mp_xor(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* c = a OR b (two complement) */ +mp_err mp_or(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* c = a AND b (two complement) */ +mp_err mp_and(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* b = ~a (bitwise not, two complement) */ +mp_err mp_complement(const mp_int *a, mp_int *b) MP_WUR; + +/* right shift with sign extension */ +mp_err mp_signed_rsh(const mp_int *a, int b, mp_int *c) MP_WUR; + +/* ---> Basic arithmetic <--- */ + +/* b = -a */ +mp_err mp_neg(const mp_int *a, mp_int *b) MP_WUR; + +/* b = |a| */ +mp_err mp_abs(const mp_int *a, mp_int *b) MP_WUR; + +/* compare a to b */ +mp_ord mp_cmp(const mp_int *a, const mp_int *b) MP_WUR; + +/* compare |a| to |b| */ +mp_ord mp_cmp_mag(const mp_int *a, const mp_int *b) MP_WUR; + +/* c = a + b */ +mp_err mp_add(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* c = a - b */ +mp_err mp_sub(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* c = a * b */ +mp_err mp_mul(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* b = a*a */ +#define mp_sqr(a, b) mp_mul((a), (a), (b)) + +/* a/b => cb + d == a */ +mp_err mp_div(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d) MP_WUR; + +/* c = a mod b, 0 <= c < b */ +mp_err mp_mod(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* Increment "a" by one like "a++". Changes input! */ +#define mp_incr(a) mp_add_d((a), 1u, (a)) + +/* Decrement "a" by one like "a--". Changes input! */ +#define mp_decr(a) mp_sub_d((a), 1u, (a)) + +/* ---> single digit functions <--- */ + +/* compare against a single digit */ +mp_ord mp_cmp_d(const mp_int *a, mp_digit b) MP_WUR; + +/* c = a + b */ +mp_err mp_add_d(const mp_int *a, mp_digit b, mp_int *c) MP_WUR; + +/* c = a - b */ +mp_err mp_sub_d(const mp_int *a, mp_digit b, mp_int *c) MP_WUR; + +/* c = a * b */ +mp_err mp_mul_d(const mp_int *a, mp_digit b, mp_int *c) MP_WUR; + +/* a/b => cb + d == a */ +mp_err mp_div_d(const mp_int *a, mp_digit b, mp_int *c, mp_digit *d) MP_WUR; + +/* c = a mod b, 0 <= c < b */ +#define mp_mod_d(a, b, c) mp_div_d((a), (b), NULL, (c)) + +/* ---> number theory <--- */ + +/* d = a + b (mod c) */ +mp_err mp_addmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d) MP_WUR; + +/* d = a - b (mod c) */ +mp_err mp_submod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d) MP_WUR; + +/* d = a * b (mod c) */ +mp_err mp_mulmod(const mp_int *a, const mp_int *b, const mp_int *c, mp_int *d) MP_WUR; + +/* c = a * a (mod b) */ +mp_err mp_sqrmod(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* c = 1/a (mod b) */ +mp_err mp_invmod(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* c = (a, b) */ +mp_err mp_gcd(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* produces value such that U1*a + U2*b = U3 */ +mp_err mp_exteuclid(const mp_int *a, const mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) MP_WUR; + +/* c = [a, b] or (a*b)/(a, b) */ +mp_err mp_lcm(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; + +/* Integer logarithm to integer base */ +mp_err mp_log_n(const mp_int *a, int base, int *c) MP_WUR; + +/* c = a**b */ +mp_err mp_expt_n(const mp_int *a, int b, mp_int *c) MP_WUR; + +/* finds one of the b'th root of a, such that |c|**b <= |a| + * + * returns error if a < 0 and b is even + */ +mp_err mp_root_n(const mp_int *a, int b, mp_int *c) MP_WUR; + +/* special sqrt algo */ +mp_err mp_sqrt(const mp_int *arg, mp_int *ret) MP_WUR; + +/* special sqrt (mod prime) */ +mp_err mp_sqrtmod_prime(const mp_int *n, const mp_int *prime, mp_int *ret) MP_WUR; + +/* is number a square? */ +mp_err mp_is_square(const mp_int *arg, bool *ret) MP_WUR; + +/* computes the Kronecker symbol c = (a | p) (like jacobi() but with {a,p} in Z */ +mp_err mp_kronecker(const mp_int *a, const mp_int *p, int *c) MP_WUR; + +/* used to setup the Barrett reduction for a given modulus b */ +mp_err mp_reduce_setup(mp_int *a, const mp_int *b) MP_WUR; + +/* Barrett Reduction, computes a (mod b) with a precomputed value c + * + * Assumes that 0 < x <= m*m, note if 0 > x > -(m*m) then you can merely + * compute the reduction as -1 * mp_reduce(mp_abs(x)) [pseudo code]. + */ +mp_err mp_reduce(mp_int *x, const mp_int *m, const mp_int *mu) MP_WUR; + +/* setups the montgomery reduction */ +mp_err mp_montgomery_setup(const mp_int *n, mp_digit *rho) MP_WUR; + +/* computes a = B**n mod b without division or multiplication useful for + * normalizing numbers in a Montgomery system. + */ +mp_err mp_montgomery_calc_normalization(mp_int *a, const mp_int *b) MP_WUR; + +/* computes x/R == x (mod N) via Montgomery Reduction */ +mp_err mp_montgomery_reduce(mp_int *x, const mp_int *n, mp_digit rho) MP_WUR; + +/* returns 1 if a is a valid DR modulus */ +bool mp_dr_is_modulus(const mp_int *a) MP_WUR; + +/* sets the value of "d" required for mp_dr_reduce */ +void mp_dr_setup(const mp_int *a, mp_digit *d); + +/* reduces a modulo n using the Diminished Radix method */ +mp_err mp_dr_reduce(mp_int *x, const mp_int *n, mp_digit k) MP_WUR; + +/* returns true if a can be reduced with mp_reduce_2k */ +bool mp_reduce_is_2k(const mp_int *a) MP_WUR; + +/* determines k value for 2k reduction */ +mp_err mp_reduce_2k_setup(const mp_int *a, mp_digit *d) MP_WUR; + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +mp_err mp_reduce_2k(mp_int *a, const mp_int *n, mp_digit d) MP_WUR; + +/* returns true if a can be reduced with mp_reduce_2k_l */ +bool mp_reduce_is_2k_l(const mp_int *a) MP_WUR; + +/* determines k value for 2k reduction */ +mp_err mp_reduce_2k_setup_l(const mp_int *a, mp_int *d) MP_WUR; + +/* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ +mp_err mp_reduce_2k_l(mp_int *a, const mp_int *n, const mp_int *d) MP_WUR; + +/* Y = G**X (mod P) */ +mp_err mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y) MP_WUR; + +/* ---> Primes <--- */ + +/* performs one Fermat test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +mp_err mp_prime_fermat(const mp_int *a, const mp_int *b, bool *result) MP_WUR; + +/* performs one Miller-Rabin test of "a" using base "b". + * Sets result to 0 if composite or 1 if probable prime + */ +mp_err mp_prime_miller_rabin(const mp_int *a, const mp_int *b, bool *result) MP_WUR; + +/* 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) MP_WUR; + +/* performs one strong Lucas-Selfridge test of "a". + * Sets result to 0 if composite or 1 if probable prime + */ +mp_err mp_prime_strong_lucas_selfridge(const mp_int *a, bool *result) MP_WUR; + +/* performs one Frobenius test of "a" as described by Paul Underwood. + * Sets result to 0 if composite or 1 if probable prime + */ +mp_err mp_prime_frobenius_underwood(const mp_int *N, bool *result) MP_WUR; + +/* performs t random rounds of Miller-Rabin on "a" additional to + * bases 2 and 3. Also performs an initial sieve of trial + * division. Determines if "a" is prime with probability + * of error no more than (1/4)**t. + * Both a strong Lucas-Selfridge to complete the BPSW test + * and a separate Frobenius test are available at compile time. + * With t<0 a deterministic test is run for primes up to + * 318665857834031151167461. With t<13 (abs(t)-13) additional + * tests with sequential small primes are run starting at 43. + * Is Fips 186.4 compliant if called with t as computed by + * mp_prime_rabin_miller_trials(); + * + * Sets result to 1 if probably prime, 0 otherwise + */ +mp_err mp_prime_is_prime(const mp_int *a, int t, bool *result) MP_WUR; + +/* finds the next prime after the number "a" using "t" trials + * of Miller-Rabin. + * + * bbs_style = true means the prime must be congruent to 3 mod 4 + */ +mp_err mp_prime_next_prime(mp_int *a, int t, bool bbs_style) MP_WUR; + +/* makes a truly random prime of a given size (bits), + * + * Flags are as follows: + * + * MP_PRIME_BBS - make prime congruent to 3 mod 4 + * MP_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies MP_PRIME_BBS) + * MP_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 + * + */ +// mp_err mp_prime_rand(mp_int *a, int t, int size, int flags) MP_WUR; + +/* ---> radix conversion <--- */ +int mp_count_bits(const mp_int *a) MP_WUR; + +size_t mp_ubin_size(const mp_int *a) MP_WUR; +mp_err mp_from_ubin(mp_int *a, const uint8_t *buf, size_t size) MP_WUR; +mp_err mp_to_ubin(const mp_int *a, uint8_t *buf, size_t maxlen, size_t *written) MP_WUR; + +size_t mp_sbin_size(const mp_int *a) MP_WUR; +mp_err mp_from_sbin(mp_int *a, const uint8_t *buf, size_t size) MP_WUR; +mp_err mp_to_sbin(const mp_int *a, uint8_t *buf, size_t maxlen, size_t *written) MP_WUR; + +mp_err mp_read_radix(mp_int *a, const char *str, int radix) MP_WUR; +mp_err mp_to_radix(const mp_int *a, char *str, size_t maxlen, size_t *written, int radix) MP_WUR; + +mp_err mp_radix_size(const mp_int *a, int radix, size_t *size) MP_WUR; +mp_err mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size) MP_WUR; + +#ifndef MP_NO_FILE +mp_err mp_fread(mp_int *a, int radix, FILE *stream) MP_WUR; +mp_err mp_fwrite(const mp_int *a, int radix, FILE *stream) MP_WUR; +#endif + +#define mp_to_binary(M, S, N) mp_to_radix((M), (S), (N), NULL, 2) +#define mp_to_octal(M, S, N) mp_to_radix((M), (S), (N), NULL, 8) +#define mp_to_decimal(M, S, N) mp_to_radix((M), (S), (N), NULL, 10) +#define mp_to_hex(M, S, N) mp_to_radix((M), (S), (N), NULL, 16) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libtommath/tommath_c89.h b/src/libtommath/tommath_c89.h new file mode 100644 index 000000000..e7b87f105 --- /dev/null +++ b/src/libtommath/tommath_c89.h @@ -0,0 +1,40 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* + * This header defines custom types which + * are used in c89 mode. + * + * By default, the source uses stdbool.h + * and stdint.h. The command `make c89` + * can be used to convert the source, + * such that this header is used instead. + * Use `make c99` to convert back. + * + * Please adapt the following definitions to your needs! + */ + +/* stdbool.h replacement types */ +typedef enum { MP_NO, MP_YES } mp_bool; + +/* stdint.h replacement types */ +typedef __INT8_TYPE__ mp_i8; +typedef __INT16_TYPE__ mp_i16; +typedef __INT32_TYPE__ mp_i32; +typedef __INT64_TYPE__ mp_i64; +typedef __UINT8_TYPE__ mp_u8; +typedef __UINT16_TYPE__ mp_u16; +typedef __UINT32_TYPE__ mp_u32; +typedef __UINT64_TYPE__ mp_u64; + +/* inttypes.h replacement, printf format specifier */ +# if __WORDSIZE == 64 +# define MP_PRI64_PREFIX "l" +# else +# define MP_PRI64_PREFIX "ll" +# endif +#define MP_PRIi64 MP_PRI64_PREFIX "i" +#define MP_PRIu64 MP_PRI64_PREFIX "u" +#define MP_PRIx64 MP_PRI64_PREFIX "x" + +#define MP_FUNCTION_NAME __func__ diff --git a/src/libtommath/tommath_class.h b/src/libtommath/tommath_class.h new file mode 100644 index 000000000..da9c5ea72 --- /dev/null +++ b/src/libtommath/tommath_class.h @@ -0,0 +1,1260 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) +#define LTM_INSIDE +#if defined(LTM2) +# define LTM3 +#endif +#if defined(LTM1) +# define LTM2 +#endif +#define LTM1 +#if defined(LTM_ALL) +# define MP_2EXPT_C +# define MP_ABS_C +# define MP_ADD_C +# define MP_ADD_D_C +# define MP_ADDMOD_C +# define MP_AND_C +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_CMP_MAG_C +# define MP_CNT_LSB_C +# define MP_COMPLEMENT_C +# define MP_COPY_C +# define MP_COUNT_BITS_C +# define MP_CUTOFFS_C +# define MP_DIV_C +# define MP_DIV_2_C +# define MP_DIV_2D_C +# define MP_DIV_D_C +# define MP_DR_IS_MODULUS_C +# define MP_DR_REDUCE_C +# define MP_DR_SETUP_C +# define MP_ERROR_TO_STRING_C +# define MP_EXCH_C +# define MP_EXPT_N_C +# define MP_EXPTMOD_C +# define MP_EXTEUCLID_C +# define MP_FREAD_C +# define MP_FROM_SBIN_C +# define MP_FROM_UBIN_C +# define MP_FWRITE_C +# define MP_GCD_C +# define MP_GET_DOUBLE_C +# define MP_GET_I32_C +# define MP_GET_I64_C +# define MP_GET_L_C +# define MP_GET_MAG_U32_C +# define MP_GET_MAG_U64_C +# define MP_GET_MAG_UL_C +# define MP_GROW_C +# define MP_INIT_C +# define MP_INIT_COPY_C +# define MP_INIT_I32_C +# define MP_INIT_I64_C +# define MP_INIT_L_C +# define MP_INIT_MULTI_C +# define MP_INIT_SET_C +# define MP_INIT_SIZE_C +# define MP_INIT_U32_C +# define MP_INIT_U64_C +# define MP_INIT_UL_C +# define MP_INVMOD_C +# define MP_IS_SQUARE_C +# define MP_KRONECKER_C +# define MP_LCM_C +# define MP_LOG_N_C +# define MP_LSHD_C +# define MP_MOD_C +# define MP_MOD_2D_C +# define MP_MONTGOMERY_CALC_NORMALIZATION_C +# define MP_MONTGOMERY_REDUCE_C +# define MP_MONTGOMERY_SETUP_C +# define MP_MUL_C +# define MP_MUL_2_C +# define MP_MUL_2D_C +# define MP_MUL_D_C +# define MP_MULMOD_C +# define MP_NEG_C +# define MP_OR_C +# define MP_PACK_C +# define MP_PACK_COUNT_C +# define MP_PRIME_FERMAT_C +# define MP_PRIME_FROBENIUS_UNDERWOOD_C +# define MP_PRIME_IS_PRIME_C +# define MP_PRIME_MILLER_RABIN_C +# define MP_PRIME_NEXT_PRIME_C +# define MP_PRIME_RABIN_MILLER_TRIALS_C +# define MP_PRIME_RAND_C +# define MP_PRIME_STRONG_LUCAS_SELFRIDGE_C +# define MP_RADIX_SIZE_C +# define MP_RADIX_SIZE_OVERESTIMATE_C +# define MP_RAND_C +# define MP_RAND_SOURCE_C +# define MP_READ_RADIX_C +# define MP_REDUCE_C +# define MP_REDUCE_2K_C +# define MP_REDUCE_2K_L_C +# define MP_REDUCE_2K_SETUP_C +# define MP_REDUCE_2K_SETUP_L_C +# define MP_REDUCE_IS_2K_C +# define MP_REDUCE_IS_2K_L_C +# define MP_REDUCE_SETUP_C +# define MP_ROOT_N_C +# define MP_RSHD_C +# define MP_SBIN_SIZE_C +# define MP_SET_C +# define MP_SET_DOUBLE_C +# define MP_SET_I32_C +# define MP_SET_I64_C +# define MP_SET_L_C +# define MP_SET_U32_C +# define MP_SET_U64_C +# define MP_SET_UL_C +# define MP_SHRINK_C +# define MP_SIGNED_RSH_C +# define MP_SQRMOD_C +# define MP_SQRT_C +# define MP_SQRTMOD_PRIME_C +# define MP_SUB_C +# define MP_SUB_D_C +# define MP_SUBMOD_C +# define MP_TO_RADIX_C +# define MP_TO_SBIN_C +# define MP_TO_UBIN_C +# define MP_UBIN_SIZE_C +# define MP_UNPACK_C +# define MP_XOR_C +# define MP_ZERO_C +# define S_MP_ADD_C +# define S_MP_COPY_DIGS_C +# define S_MP_DIV_3_C +# define S_MP_DIV_RECURSIVE_C +# define S_MP_DIV_SCHOOL_C +# define S_MP_DIV_SMALL_C +# define S_MP_EXPTMOD_C +# define S_MP_EXPTMOD_FAST_C +# define S_MP_GET_BIT_C +# define S_MP_INVMOD_C +# define S_MP_INVMOD_ODD_C +# define S_MP_LOG_C +# define S_MP_LOG_2EXPT_C +# define S_MP_LOG_D_C +# define S_MP_MONTGOMERY_REDUCE_COMBA_C +# define S_MP_MUL_C +# define S_MP_MUL_BALANCE_C +# define S_MP_MUL_COMBA_C +# define S_MP_MUL_HIGH_C +# define S_MP_MUL_HIGH_COMBA_C +# define S_MP_MUL_KARATSUBA_C +# define S_MP_MUL_TOOM_C +# define S_MP_PRIME_IS_DIVISIBLE_C +# define S_MP_PRIME_TAB_C +# define S_MP_RADIX_MAP_C +# define S_MP_RADIX_SIZE_OVERESTIMATE_C +# define S_MP_RAND_PLATFORM_C +# define S_MP_SQR_C +# define S_MP_SQR_COMBA_C +# define S_MP_SQR_KARATSUBA_C +# define S_MP_SQR_TOOM_C +# define S_MP_SUB_C +# define S_MP_ZERO_BUF_C +# define S_MP_ZERO_DIGS_C +#endif +#endif +#if defined(MP_2EXPT_C) +# define MP_GROW_C +# define MP_ZERO_C +#endif + +#if defined(MP_ABS_C) +# define MP_COPY_C +#endif + +#if defined(MP_ADD_C) +# define MP_CMP_MAG_C +# define S_MP_ADD_C +# define S_MP_SUB_C +#endif + +#if defined(MP_ADD_D_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define MP_SUB_D_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_ADDMOD_C) +# define MP_ADD_C +# define MP_MOD_C +#endif + +#if defined(MP_AND_C) +# define MP_CLAMP_C +# define MP_GROW_C +#endif + +#if defined(MP_CLAMP_C) +#endif + +#if defined(MP_CLEAR_C) +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_CLEAR_MULTI_C) +# define MP_CLEAR_C +#endif + +#if defined(MP_CMP_C) +# define MP_CMP_MAG_C +#endif + +#if defined(MP_CMP_D_C) +#endif + +#if defined(MP_CMP_MAG_C) +#endif + +#if defined(MP_CNT_LSB_C) +#endif + +#if defined(MP_COMPLEMENT_C) +# define MP_SUB_D_C +#endif + +#if defined(MP_COPY_C) +# define MP_GROW_C +# define S_MP_COPY_DIGS_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_COUNT_BITS_C) +#endif + +#if defined(MP_CUTOFFS_C) +#endif + +#if defined(MP_DIV_C) +# define MP_CMP_MAG_C +# define MP_COPY_C +# define MP_ZERO_C +# define S_MP_DIV_RECURSIVE_C +# define S_MP_DIV_SCHOOL_C +# define S_MP_DIV_SMALL_C +#endif + +#if defined(MP_DIV_2_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_DIV_2D_C) +# define MP_CLAMP_C +# define MP_COPY_C +# define MP_MOD_2D_C +# define MP_RSHD_C +#endif + +#if defined(MP_DIV_D_C) +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_COPY_C +# define MP_DIV_2D_C +# define MP_DIV_2_C +# define MP_EXCH_C +# define MP_INIT_SIZE_C +# define S_MP_DIV_3_C +#endif + +#if defined(MP_DR_IS_MODULUS_C) +#endif + +#if defined(MP_DR_REDUCE_C) +# define MP_CLAMP_C +# define MP_CMP_MAG_C +# define MP_GROW_C +# define S_MP_SUB_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_DR_SETUP_C) +#endif + +#if defined(MP_ERROR_TO_STRING_C) +#endif + +#if defined(MP_EXCH_C) +#endif + +#if defined(MP_EXPT_N_C) +# define MP_CLEAR_C +# define MP_INIT_COPY_C +# define MP_MUL_C +# define MP_SET_C +#endif + +#if defined(MP_EXPTMOD_C) +# define MP_ABS_C +# define MP_CLEAR_MULTI_C +# define MP_DR_IS_MODULUS_C +# define MP_INIT_MULTI_C +# define MP_INVMOD_C +# define MP_REDUCE_IS_2K_C +# define MP_REDUCE_IS_2K_L_C +# define S_MP_EXPTMOD_C +# define S_MP_EXPTMOD_FAST_C +#endif + +#if defined(MP_EXTEUCLID_C) +# define MP_CLEAR_MULTI_C +# define MP_COPY_C +# define MP_DIV_C +# define MP_EXCH_C +# define MP_INIT_MULTI_C +# define MP_MUL_C +# define MP_NEG_C +# define MP_SET_C +# define MP_SUB_C +#endif + +#if defined(MP_FREAD_C) +# define MP_ADD_D_C +# define MP_MUL_D_C +# define MP_ZERO_C +#endif + +#if defined(MP_FROM_SBIN_C) +# define MP_FROM_UBIN_C +#endif + +#if defined(MP_FROM_UBIN_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define MP_MUL_2D_C +# define MP_ZERO_C +#endif + +#if defined(MP_FWRITE_C) +# define MP_RADIX_SIZE_OVERESTIMATE_C +# define MP_TO_RADIX_C +# define S_MP_ZERO_BUF_C +#endif + +#if defined(MP_GCD_C) +# define MP_ABS_C +# define MP_CLEAR_C +# define MP_CMP_MAG_C +# define MP_CNT_LSB_C +# define MP_DIV_2D_C +# define MP_EXCH_C +# define MP_INIT_COPY_C +# define MP_MUL_2D_C +# define S_MP_SUB_C +#endif + +#if defined(MP_GET_DOUBLE_C) +#endif + +#if defined(MP_GET_I32_C) +# define MP_GET_MAG_U32_C +#endif + +#if defined(MP_GET_I64_C) +# define MP_GET_MAG_U64_C +#endif + +#if defined(MP_GET_L_C) +# define MP_GET_MAG_UL_C +#endif + +#if defined(MP_GET_MAG_U32_C) +#endif + +#if defined(MP_GET_MAG_U64_C) +#endif + +#if defined(MP_GET_MAG_UL_C) +#endif + +#if defined(MP_GROW_C) +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_INIT_C) +#endif + +#if defined(MP_INIT_COPY_C) +# define MP_CLEAR_C +# define MP_COPY_C +# define MP_INIT_SIZE_C +#endif + +#if defined(MP_INIT_I32_C) +# define MP_INIT_C +# define MP_SET_I32_C +#endif + +#if defined(MP_INIT_I64_C) +# define MP_INIT_C +# define MP_SET_I64_C +#endif + +#if defined(MP_INIT_L_C) +# define MP_INIT_C +# define MP_SET_L_C +#endif + +#if defined(MP_INIT_MULTI_C) +# define MP_CLEAR_C +# define MP_INIT_C +#endif + +#if defined(MP_INIT_SET_C) +# define MP_INIT_C +# define MP_SET_C +#endif + +#if defined(MP_INIT_SIZE_C) +#endif + +#if defined(MP_INIT_U32_C) +# define MP_INIT_C +# define MP_SET_U32_C +#endif + +#if defined(MP_INIT_U64_C) +# define MP_INIT_C +# define MP_SET_U64_C +#endif + +#if defined(MP_INIT_UL_C) +# define MP_INIT_C +# define MP_SET_UL_C +#endif + +#if defined(MP_INVMOD_C) +# define MP_CMP_D_C +# define MP_ZERO_C +# define S_MP_INVMOD_C +# define S_MP_INVMOD_ODD_C +#endif + +#if defined(MP_IS_SQUARE_C) +# define MP_CLEAR_C +# define MP_CMP_MAG_C +# define MP_DIV_D_C +# define MP_GET_I32_C +# define MP_INIT_U32_C +# define MP_MOD_C +# define MP_MUL_C +# define MP_SQRT_C +#endif + +#if defined(MP_KRONECKER_C) +# define MP_CLEAR_C +# define MP_CMP_D_C +# define MP_CNT_LSB_C +# define MP_COPY_C +# define MP_DIV_2D_C +# define MP_INIT_C +# define MP_INIT_COPY_C +# define MP_MOD_C +#endif + +#if defined(MP_LCM_C) +# define MP_CLEAR_MULTI_C +# define MP_CMP_MAG_C +# define MP_DIV_C +# define MP_GCD_C +# define MP_INIT_MULTI_C +# define MP_MUL_C +#endif + +#if defined(MP_LOG_N_C) +# define S_MP_LOG_2EXPT_C +# define S_MP_LOG_C +# define S_MP_LOG_D_C +#endif + +#if defined(MP_LSHD_C) +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_MOD_C) +# define MP_ADD_C +# define MP_DIV_C +#endif + +#if defined(MP_MOD_2D_C) +# define MP_CLAMP_C +# define MP_COPY_C +# define MP_ZERO_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_MONTGOMERY_CALC_NORMALIZATION_C) +# define MP_2EXPT_C +# define MP_CMP_MAG_C +# define MP_COUNT_BITS_C +# define MP_MUL_2_C +# define MP_SET_C +# define S_MP_SUB_C +#endif + +#if defined(MP_MONTGOMERY_REDUCE_C) +# define MP_CLAMP_C +# define MP_CMP_MAG_C +# define MP_GROW_C +# define MP_RSHD_C +# define S_MP_MONTGOMERY_REDUCE_COMBA_C +# define S_MP_SUB_C +#endif + +#if defined(MP_MONTGOMERY_SETUP_C) +#endif + +#if defined(MP_MUL_C) +# define S_MP_MUL_BALANCE_C +# define S_MP_MUL_C +# define S_MP_MUL_COMBA_C +# define S_MP_MUL_KARATSUBA_C +# define S_MP_MUL_TOOM_C +# define S_MP_SQR_C +# define S_MP_SQR_COMBA_C +# define S_MP_SQR_KARATSUBA_C +# define S_MP_SQR_TOOM_C +#endif + +#if defined(MP_MUL_2_C) +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_MUL_2D_C) +# define MP_CLAMP_C +# define MP_COPY_C +# define MP_GROW_C +# define MP_LSHD_C +#endif + +#if defined(MP_MUL_D_C) +# define MP_CLAMP_C +# define MP_COPY_C +# define MP_GROW_C +# define MP_MUL_2D_C +# define MP_MUL_2_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_MULMOD_C) +# define MP_MOD_C +# define MP_MUL_C +#endif + +#if defined(MP_NEG_C) +# define MP_COPY_C +#endif + +#if defined(MP_OR_C) +# define MP_CLAMP_C +# define MP_GROW_C +#endif + +#if defined(MP_PACK_C) +# define MP_CLEAR_C +# define MP_DIV_2D_C +# define MP_INIT_COPY_C +# define MP_PACK_COUNT_C +#endif + +#if defined(MP_PACK_COUNT_C) +# define MP_COUNT_BITS_C +#endif + +#if defined(MP_PRIME_FERMAT_C) +# define MP_CLEAR_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_EXPTMOD_C +# define MP_INIT_C +#endif + +#if defined(MP_PRIME_FROBENIUS_UNDERWOOD_C) +# define MP_ADD_C +# define MP_ADD_D_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_COUNT_BITS_C +# define MP_EXCH_C +# define MP_GCD_C +# define MP_INIT_MULTI_C +# define MP_KRONECKER_C +# define MP_MOD_C +# define MP_MUL_2_C +# define MP_MUL_C +# define MP_MUL_D_C +# define MP_SET_C +# define MP_SET_I32_C +# define MP_SET_U32_C +# define MP_SUB_C +# define S_MP_GET_BIT_C +#endif + +#if defined(MP_PRIME_IS_PRIME_C) +# define MP_CLEAR_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_COUNT_BITS_C +# define MP_DIV_2D_C +# define MP_INIT_SET_C +# define MP_IS_SQUARE_C +# define MP_PRIME_MILLER_RABIN_C +# define MP_PRIME_STRONG_LUCAS_SELFRIDGE_C +# define MP_RAND_C +# define MP_READ_RADIX_C +# define MP_SET_C +# define S_MP_PRIME_IS_DIVISIBLE_C +#endif + +#if defined(MP_PRIME_MILLER_RABIN_C) +# define MP_CLEAR_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_CNT_LSB_C +# define MP_DIV_2D_C +# define MP_EXPTMOD_C +# define MP_INIT_C +# define MP_INIT_COPY_C +# define MP_SQRMOD_C +# define MP_SUB_D_C +#endif + +#if defined(MP_PRIME_NEXT_PRIME_C) +# define MP_ADD_D_C +# define MP_CLEAR_C +# define MP_CMP_D_C +# define MP_DIV_D_C +# define MP_INIT_C +# define MP_PRIME_IS_PRIME_C +# define MP_SET_C +# define MP_SUB_D_C +#endif + +#if defined(MP_PRIME_RABIN_MILLER_TRIALS_C) +#endif + +#if defined(MP_PRIME_RAND_C) +# define MP_ADD_D_C +# define MP_DIV_2_C +# define MP_FROM_UBIN_C +# define MP_MUL_2_C +# define MP_PRIME_IS_PRIME_C +# define MP_SUB_D_C +# define S_MP_RAND_SOURCE_C +# define S_MP_ZERO_BUF_C +#endif + +#if defined(MP_PRIME_STRONG_LUCAS_SELFRIDGE_C) +# define MP_ADD_C +# define MP_ADD_D_C +# define MP_CLEAR_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_CNT_LSB_C +# define MP_COUNT_BITS_C +# define MP_DIV_2D_C +# define MP_DIV_2_C +# define MP_GCD_C +# define MP_INIT_C +# define MP_INIT_MULTI_C +# define MP_KRONECKER_C +# define MP_MOD_C +# define MP_MUL_2_C +# define MP_MUL_C +# define MP_SET_C +# define MP_SET_I32_C +# define MP_SET_U32_C +# define MP_SUB_C +# define MP_SUB_D_C +# define S_MP_GET_BIT_C +#endif + +#if defined(MP_RADIX_SIZE_C) +# define MP_LOG_N_C +#endif + +#if defined(MP_RADIX_SIZE_OVERESTIMATE_C) +# define MP_RADIX_SIZE_C +# define S_MP_RADIX_SIZE_OVERESTIMATE_C +#endif + +#if defined(MP_RAND_C) +# define MP_GROW_C +# define MP_ZERO_C +# define S_MP_RAND_SOURCE_C +#endif + +#if defined(MP_RAND_SOURCE_C) +# define S_MP_RAND_PLATFORM_C +#endif + +#if defined(MP_READ_RADIX_C) +# define MP_ADD_D_C +# define MP_MUL_D_C +# define MP_ZERO_C +#endif + +#if defined(MP_REDUCE_C) +# define MP_ADD_C +# define MP_CLEAR_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_INIT_COPY_C +# define MP_LSHD_C +# define MP_MOD_2D_C +# define MP_MUL_C +# define MP_RSHD_C +# define MP_SET_C +# define MP_SUB_C +# define S_MP_MUL_C +# define S_MP_MUL_HIGH_C +# define S_MP_MUL_HIGH_COMBA_C +# define S_MP_SUB_C +#endif + +#if defined(MP_REDUCE_2K_C) +# define MP_CLEAR_C +# define MP_CMP_MAG_C +# define MP_COUNT_BITS_C +# define MP_DIV_2D_C +# define MP_INIT_C +# define MP_MUL_D_C +# define S_MP_ADD_C +# define S_MP_SUB_C +#endif + +#if defined(MP_REDUCE_2K_L_C) +# define MP_CLEAR_C +# define MP_CMP_MAG_C +# define MP_COUNT_BITS_C +# define MP_DIV_2D_C +# define MP_INIT_C +# define MP_MUL_C +# define S_MP_ADD_C +# define S_MP_SUB_C +#endif + +#if defined(MP_REDUCE_2K_SETUP_C) +# define MP_2EXPT_C +# define MP_CLEAR_C +# define MP_COUNT_BITS_C +# define MP_INIT_C +# define S_MP_SUB_C +#endif + +#if defined(MP_REDUCE_2K_SETUP_L_C) +# define MP_2EXPT_C +# define MP_CLEAR_C +# define MP_COUNT_BITS_C +# define MP_INIT_C +# define S_MP_SUB_C +#endif + +#if defined(MP_REDUCE_IS_2K_C) +# define MP_COUNT_BITS_C +#endif + +#if defined(MP_REDUCE_IS_2K_L_C) +#endif + +#if defined(MP_REDUCE_SETUP_C) +# define MP_2EXPT_C +# define MP_DIV_C +#endif + +#if defined(MP_ROOT_N_C) +# define MP_2EXPT_C +# define MP_ADD_D_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_COPY_C +# define MP_COUNT_BITS_C +# define MP_DIV_C +# define MP_EXCH_C +# define MP_EXPT_N_C +# define MP_INIT_MULTI_C +# define MP_MUL_C +# define MP_MUL_D_C +# define MP_SET_C +# define MP_SUB_C +# define MP_SUB_D_C +#endif + +#if defined(MP_RSHD_C) +# define MP_ZERO_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_SBIN_SIZE_C) +# define MP_UBIN_SIZE_C +#endif + +#if defined(MP_SET_C) +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_SET_DOUBLE_C) +# define MP_DIV_2D_C +# define MP_MUL_2D_C +# define MP_SET_U64_C +#endif + +#if defined(MP_SET_I32_C) +# define MP_SET_U32_C +#endif + +#if defined(MP_SET_I64_C) +# define MP_SET_U64_C +#endif + +#if defined(MP_SET_L_C) +# define MP_SET_UL_C +#endif + +#if defined(MP_SET_U32_C) +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_SET_U64_C) +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_SET_UL_C) +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_SHRINK_C) +#endif + +#if defined(MP_SIGNED_RSH_C) +# define MP_ADD_D_C +# define MP_DIV_2D_C +# define MP_SUB_D_C +#endif + +#if defined(MP_SQRMOD_C) +# define MP_MOD_C +# define MP_MUL_C +#endif + +#if defined(MP_SQRT_C) +# define MP_ADD_C +# define MP_CLEAR_C +# define MP_CMP_MAG_C +# define MP_DIV_2_C +# define MP_DIV_C +# define MP_EXCH_C +# define MP_INIT_C +# define MP_INIT_COPY_C +# define MP_RSHD_C +# define MP_ZERO_C +#endif + +#if defined(MP_SQRTMOD_PRIME_C) +# define MP_ADD_D_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_D_C +# define MP_COPY_C +# define MP_DIV_2_C +# define MP_EXPTMOD_C +# define MP_INIT_MULTI_C +# define MP_KRONECKER_C +# define MP_MULMOD_C +# define MP_SET_C +# define MP_SET_I32_C +# define MP_SQRMOD_C +# define MP_SUB_D_C +# define MP_ZERO_C +#endif + +#if defined(MP_SUB_C) +# define MP_CMP_MAG_C +# define S_MP_ADD_C +# define S_MP_SUB_C +#endif + +#if defined(MP_SUB_D_C) +# define MP_ADD_D_C +# define MP_CLAMP_C +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(MP_SUBMOD_C) +# define MP_MOD_C +# define MP_SUB_C +#endif + +#if defined(MP_TO_RADIX_C) +# define MP_CLEAR_C +# define MP_DIV_D_C +# define MP_INIT_COPY_C +#endif + +#if defined(MP_TO_SBIN_C) +# define MP_TO_UBIN_C +#endif + +#if defined(MP_TO_UBIN_C) +# define MP_CLEAR_C +# define MP_DIV_2D_C +# define MP_INIT_COPY_C +# define MP_UBIN_SIZE_C +#endif + +#if defined(MP_UBIN_SIZE_C) +# define MP_COUNT_BITS_C +#endif + +#if defined(MP_UNPACK_C) +# define MP_CLAMP_C +# define MP_MUL_2D_C +# define MP_ZERO_C +#endif + +#if defined(MP_XOR_C) +# define MP_CLAMP_C +# define MP_GROW_C +#endif + +#if defined(MP_ZERO_C) +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(S_MP_ADD_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(S_MP_COPY_DIGS_C) +#endif + +#if defined(S_MP_DIV_3_C) +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_EXCH_C +# define MP_INIT_SIZE_C +#endif + +#if defined(S_MP_DIV_RECURSIVE_C) +# define MP_ADD_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_D_C +# define MP_COPY_C +# define MP_DIV_2D_C +# define MP_EXCH_C +# define MP_INIT_MULTI_C +# define MP_LSHD_C +# define MP_MUL_2D_C +# define MP_MUL_C +# define MP_SUB_C +# define MP_SUB_D_C +# define MP_ZERO_C +# define S_MP_DIV_SCHOOL_C +#endif + +#if defined(S_MP_DIV_SCHOOL_C) +# define MP_ADD_C +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_CMP_C +# define MP_CMP_MAG_C +# define MP_COPY_C +# define MP_COUNT_BITS_C +# define MP_DIV_2D_C +# define MP_EXCH_C +# define MP_INIT_C +# define MP_INIT_COPY_C +# define MP_INIT_SIZE_C +# define MP_LSHD_C +# define MP_MUL_2D_C +# define MP_MUL_D_C +# define MP_RSHD_C +# define MP_SUB_C +# define MP_ZERO_C +#endif + +#if defined(S_MP_DIV_SMALL_C) +# define MP_ABS_C +# define MP_ADD_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_COUNT_BITS_C +# define MP_DIV_2D_C +# define MP_EXCH_C +# define MP_INIT_MULTI_C +# define MP_MUL_2D_C +# define MP_SET_C +# define MP_SUB_C +#endif + +#if defined(S_MP_EXPTMOD_C) +# define MP_CLEAR_C +# define MP_COPY_C +# define MP_COUNT_BITS_C +# define MP_EXCH_C +# define MP_INIT_C +# define MP_MOD_C +# define MP_MUL_C +# define MP_REDUCE_2K_L_C +# define MP_REDUCE_2K_SETUP_L_C +# define MP_REDUCE_C +# define MP_REDUCE_SETUP_C +# define MP_SET_C +#endif + +#if defined(S_MP_EXPTMOD_FAST_C) +# define MP_CLEAR_C +# define MP_COPY_C +# define MP_COUNT_BITS_C +# define MP_DR_REDUCE_C +# define MP_DR_SETUP_C +# define MP_EXCH_C +# define MP_INIT_SIZE_C +# define MP_MOD_C +# define MP_MONTGOMERY_CALC_NORMALIZATION_C +# define MP_MONTGOMERY_REDUCE_C +# define MP_MONTGOMERY_SETUP_C +# define MP_MULMOD_C +# define MP_MUL_C +# define MP_REDUCE_2K_C +# define MP_REDUCE_2K_SETUP_C +# define MP_SET_C +# define S_MP_MONTGOMERY_REDUCE_COMBA_C +#endif + +#if defined(S_MP_GET_BIT_C) +#endif + +#if defined(S_MP_INVMOD_C) +# define MP_ADD_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_CMP_MAG_C +# define MP_COPY_C +# define MP_DIV_2_C +# define MP_EXCH_C +# define MP_INIT_MULTI_C +# define MP_MOD_C +# define MP_SET_C +# define MP_SUB_C +#endif + +#if defined(S_MP_INVMOD_ODD_C) +# define MP_ADD_C +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_CMP_MAG_C +# define MP_COPY_C +# define MP_DIV_2_C +# define MP_EXCH_C +# define MP_INIT_MULTI_C +# define MP_MOD_C +# define MP_SET_C +# define MP_SUB_C +#endif + +#if defined(S_MP_LOG_C) +# define MP_CLEAR_MULTI_C +# define MP_CMP_C +# define MP_CMP_D_C +# define MP_COPY_C +# define MP_EXCH_C +# define MP_EXPT_N_C +# define MP_INIT_MULTI_C +# define MP_MUL_C +# define MP_SET_C +#endif + +#if defined(S_MP_LOG_2EXPT_C) +# define MP_COUNT_BITS_C +#endif + +#if defined(S_MP_LOG_D_C) +#endif + +#if defined(S_MP_MONTGOMERY_REDUCE_COMBA_C) +# define MP_CLAMP_C +# define MP_CMP_MAG_C +# define MP_GROW_C +# define S_MP_SUB_C +# define S_MP_ZERO_BUF_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(S_MP_MUL_C) +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_EXCH_C +# define MP_INIT_SIZE_C +# define S_MP_MUL_COMBA_C +#endif + +#if defined(S_MP_MUL_BALANCE_C) +# define MP_ADD_C +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_CLEAR_MULTI_C +# define MP_EXCH_C +# define MP_INIT_MULTI_C +# define MP_INIT_SIZE_C +# define MP_LSHD_C +# define MP_MUL_C +# define S_MP_COPY_DIGS_C +#endif + +#if defined(S_MP_MUL_COMBA_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(S_MP_MUL_HIGH_C) +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_EXCH_C +# define MP_INIT_SIZE_C +# define S_MP_MUL_HIGH_COMBA_C +#endif + +#if defined(S_MP_MUL_HIGH_COMBA_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(S_MP_MUL_KARATSUBA_C) +# define MP_ADD_C +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_INIT_SIZE_C +# define MP_LSHD_C +# define MP_MUL_C +# define S_MP_ADD_C +# define S_MP_COPY_DIGS_C +# define S_MP_SUB_C +#endif + +#if defined(S_MP_MUL_TOOM_C) +# define MP_ADD_C +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_CLEAR_MULTI_C +# define MP_DIV_2_C +# define MP_INIT_MULTI_C +# define MP_INIT_SIZE_C +# define MP_LSHD_C +# define MP_MUL_2_C +# define MP_MUL_C +# define MP_SUB_C +# define S_MP_COPY_DIGS_C +# define S_MP_DIV_3_C +#endif + +#if defined(S_MP_PRIME_IS_DIVISIBLE_C) +# define MP_DIV_D_C +#endif + +#if defined(S_MP_PRIME_TAB_C) +#endif + +#if defined(S_MP_RADIX_MAP_C) +#endif + +#if defined(S_MP_RADIX_SIZE_OVERESTIMATE_C) +# define MP_CLEAR_MULTI_C +# define MP_COUNT_BITS_C +# define MP_DIV_2D_C +# define MP_GET_I64_C +# define MP_INIT_MULTI_C +# define MP_MUL_C +# define MP_SET_U32_C +# define S_MP_LOG_2EXPT_C +#endif + +#if defined(S_MP_RAND_PLATFORM_C) +#endif + +#if defined(S_MP_SQR_C) +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_EXCH_C +# define MP_INIT_SIZE_C +#endif + +#if defined(S_MP_SQR_COMBA_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(S_MP_SQR_KARATSUBA_C) +# define MP_ADD_C +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_INIT_SIZE_C +# define MP_LSHD_C +# define MP_MUL_C +# define S_MP_ADD_C +# define S_MP_COPY_DIGS_C +# define S_MP_SUB_C +#endif + +#if defined(S_MP_SQR_TOOM_C) +# define MP_ADD_C +# define MP_CLAMP_C +# define MP_CLEAR_C +# define MP_DIV_2_C +# define MP_INIT_C +# define MP_INIT_SIZE_C +# define MP_LSHD_C +# define MP_MUL_2_C +# define MP_MUL_C +# define MP_SUB_C +# define S_MP_COPY_DIGS_C +#endif + +#if defined(S_MP_SUB_C) +# define MP_CLAMP_C +# define MP_GROW_C +# define S_MP_ZERO_DIGS_C +#endif + +#if defined(S_MP_ZERO_BUF_C) +#endif + +#if defined(S_MP_ZERO_DIGS_C) +#endif + +#ifdef LTM_INSIDE +#undef LTM_INSIDE +#ifdef LTM3 +# define LTM_LAST +#endif + +#include "tommath_superclass.h" +#include "tommath_class.h" +#else +# define LTM_LAST +#endif diff --git a/src/libtommath/tommath_cutoffs.h b/src/libtommath/tommath_cutoffs.h new file mode 100644 index 000000000..fb8416013 --- /dev/null +++ b/src/libtommath/tommath_cutoffs.h @@ -0,0 +1,13 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +/* + Current values evaluated on an AMD A8-6600K (64-bit). + Type "make tune" to optimize them for your machine but + be aware that it may take a long time. It took 2:30 minutes + on the aforementioned machine for example. + */ + +#define MP_DEFAULT_MUL_KARATSUBA_CUTOFF 80 +#define MP_DEFAULT_SQR_KARATSUBA_CUTOFF 120 +#define MP_DEFAULT_MUL_TOOM_CUTOFF 350 +#define MP_DEFAULT_SQR_TOOM_CUTOFF 400 diff --git a/src/libtommath/tommath_private.h b/src/libtommath/tommath_private.h new file mode 100644 index 000000000..42920519c --- /dev/null +++ b/src/libtommath/tommath_private.h @@ -0,0 +1,279 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#ifndef TOMMATH_PRIVATE_H_ +#define TOMMATH_PRIVATE_H_ + +#include "tommath.h" +#include "tommath_class.h" +#include + +/* + * Private symbols + * --------------- + * + * On Unix symbols can be marked as hidden if libtommath is compiled + * as a shared object. By default, symbols are visible. + * On Win32 a .def file must be used to specify the exported symbols. + */ +#if defined(__GNUC__) && __GNUC__ >= 4 && !defined(_WIN32) && !defined(__CYGWIN__) +# define MP_PRIVATE __attribute__ ((visibility ("hidden"))) +#else +# define MP_PRIVATE +#endif + +/* Hardening libtommath + * -------------------- + * + * By default memory is zeroed before calling + * MP_FREE to avoid leaking data. This is good + * practice in cryptographical applications. + * + * Note however that memory allocators used + * in cryptographical applications can often + * be configured by itself to clear memory, + * rendering the clearing in tommath unnecessary. + * See for example https://github.com/GrapheneOS/hardened_malloc + * and the option CONFIG_ZERO_ON_FREE. + * + * Furthermore there are applications which + * value performance more and want this + * feature to be disabled. For such applications + * define MP_NO_ZERO_ON_FREE during compilation. + */ +#ifdef MP_NO_ZERO_ON_FREE +# define MP_FREE_BUF(mem, size) MP_FREE((mem), (size)) +# define MP_FREE_DIGS(mem, digits) MP_FREE((mem), sizeof (mp_digit) * (size_t)(digits)) +#else +# define MP_FREE_BUF(mem, size) \ +do { \ + size_t fs_ = (size); \ + void* fm_ = (mem); \ + if (fm_ != NULL) { \ + s_mp_zero_buf(fm_, fs_); \ + MP_FREE(fm_, fs_); \ + } \ +} while (0) +# define MP_FREE_DIGS(mem, digits) \ +do { \ + int fd_ = (digits); \ + mp_digit* fm_ = (mem); \ + if (fm_ != NULL) { \ + s_mp_zero_digs(fm_, fd_); \ + MP_FREE(fm_, sizeof (mp_digit) * (size_t)fd_); \ + } \ +} while (0) +#endif + +/* Tunable cutoffs + * --------------- + * + * - In the default settings, a cutoff X can be modified at runtime + * by adjusting the corresponding X_CUTOFF variable. + * + * - Tunability of the library can be disabled at compile time + * by defining the MP_FIXED_CUTOFFS macro. + * + * - There is an additional file tommath_cutoffs.h, which defines + * the default cutoffs. These can be adjusted manually or by the + * autotuner. + * + */ + +#ifdef MP_FIXED_CUTOFFS +# include "tommath_cutoffs.h" +# define MP_MUL_KARATSUBA_CUTOFF MP_DEFAULT_MUL_KARATSUBA_CUTOFF +# define MP_SQR_KARATSUBA_CUTOFF MP_DEFAULT_SQR_KARATSUBA_CUTOFF +# define MP_MUL_TOOM_CUTOFF MP_DEFAULT_MUL_TOOM_CUTOFF +# define MP_SQR_TOOM_CUTOFF MP_DEFAULT_SQR_TOOM_CUTOFF +#endif + +/* define heap macros */ +#ifndef MP_MALLOC +/* default to libc stuff */ +# include +# define MP_MALLOC(size) malloc(size) +# define MP_REALLOC(mem, oldsize, newsize) realloc((mem), (newsize)) +# define MP_CALLOC(nmemb, size) calloc((nmemb), (size)) +# define MP_FREE(mem, size) free(mem) +#else +/* prototypes for our heap functions */ +extern void *MP_MALLOC(size_t size); +extern void *MP_REALLOC(void *mem, size_t oldsize, size_t newsize); +extern void *MP_CALLOC(size_t nmemb, size_t size); +extern void MP_FREE(void *mem, size_t size); +#endif + +/* feature detection macro */ +#ifdef _MSC_VER +/* Prevent false positive: not enough arguments for function-like macro invocation */ +#pragma warning(disable: 4003) +#endif +#define MP_STRINGIZE(x) MP__STRINGIZE(x) +#define MP__STRINGIZE(x) ""#x"" +#define MP_HAS(x) (sizeof(MP_STRINGIZE(x##_C)) == 1u) + +#define MP_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define MP_MAX(x, y) (((x) > (y)) ? (x) : (y)) + +#define MP_TOUPPER(c) ((((c) >= 'a') && ((c) <= 'z')) ? (((c) + 'A') - 'a') : (c)) + +#define MP_EXCH(t, a, b) do { t _c = a; a = b; b = _c; } while (0) + +#define MP_IS_2EXPT(x) (((x) != 0u) && (((x) & ((x) - 1u)) == 0u)) + +/* Static assertion */ +#define MP_STATIC_ASSERT(msg, cond) typedef char mp_static_assert_##msg[(cond) ? 1 : -1]; + +#define MP_SIZEOF_BITS(type) ((size_t)CHAR_BIT * sizeof(type)) + +#define MP_MAX_COMBA (int)(1uL << (MP_SIZEOF_BITS(mp_word) - (2u * (size_t)MP_DIGIT_BIT))) +#define MP_WARRAY (int)(1uL << ((MP_SIZEOF_BITS(mp_word) - (2u * (size_t)MP_DIGIT_BIT)) + 1u)) + +#if defined(MP_16BIT) +typedef uint32_t mp_word; +#elif defined(MP_64BIT) +typedef unsigned long mp_word __attribute__((mode(TI))); +#else +typedef uint64_t mp_word; +#endif + +MP_STATIC_ASSERT(correct_word_size, sizeof(mp_word) == (2u * sizeof(mp_digit))) + +/* default number of digits */ +#ifndef MP_DEFAULT_DIGIT_COUNT +# ifndef MP_LOW_MEM +# define MP_DEFAULT_DIGIT_COUNT 32 +# else +# define MP_DEFAULT_DIGIT_COUNT 8 +# endif +#endif + +/* Minimum number of available digits in mp_int, MP_DEFAULT_DIGIT_COUNT >= MP_MIN_DIGIT_COUNT + * - Must be at least 3 for s_mp_div_school. + * - Must be large enough such that the mp_set_u64 setter can + * store uint64_t in the mp_int without growing + */ +#define MP_MIN_DIGIT_COUNT MP_MAX(3, (((int)MP_SIZEOF_BITS(uint64_t) + MP_DIGIT_BIT) - 1) / MP_DIGIT_BIT) +MP_STATIC_ASSERT(prec_geq_min_prec, MP_DEFAULT_DIGIT_COUNT >= MP_MIN_DIGIT_COUNT) + +/* Maximum number of digits. + * - Must be small enough such that mp_bit_count does not overflow. + * - Must be small enough such that mp_radix_size for base 2 does not overflow. + * mp_radix_size needs two additional bytes for zero termination and sign. + */ +#define MP_MAX_DIGIT_COUNT ((INT_MAX - 2) / MP_DIGIT_BIT) + +#if defined(__STDC_IEC_559__) || defined(__GCC_IEC_559) \ + || defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) \ + || defined(__i386__) || defined(_M_X86) || defined(_M_IX86) \ + || defined(__aarch64__) || defined(__arm__) +#define MP_HAS_SET_DOUBLE +#endif + +/* random number source */ +extern MP_PRIVATE mp_err(*s_mp_rand_source)(void *out, size_t size); + +/* lowlevel functions, do not call! */ +MP_PRIVATE bool s_mp_get_bit(const mp_int *a, int b) MP_WUR; +MP_PRIVATE int s_mp_log_2expt(const mp_int *a, mp_digit base) MP_WUR; +MP_PRIVATE int s_mp_log_d(mp_digit base, mp_digit n) MP_WUR; +MP_PRIVATE mp_err s_mp_add(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; +MP_PRIVATE mp_err s_mp_div_3(const mp_int *a, mp_int *c, mp_digit *d) MP_WUR; +MP_PRIVATE mp_err s_mp_div_recursive(const mp_int *a, const mp_int *b, mp_int *q, mp_int *r) MP_WUR; +MP_PRIVATE mp_err s_mp_div_school(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d) MP_WUR; +MP_PRIVATE mp_err s_mp_div_small(const mp_int *a, const mp_int *b, mp_int *c, mp_int *d) MP_WUR; +MP_PRIVATE mp_err s_mp_exptmod(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode) MP_WUR; +MP_PRIVATE mp_err s_mp_exptmod_fast(const mp_int *G, const mp_int *X, const mp_int *P, mp_int *Y, int redmode) MP_WUR; +MP_PRIVATE mp_err s_mp_invmod(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; +MP_PRIVATE mp_err s_mp_invmod_odd(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; +MP_PRIVATE mp_err s_mp_log(const mp_int *a, mp_digit base, int *c) MP_WUR; +MP_PRIVATE mp_err s_mp_montgomery_reduce_comba(mp_int *x, const mp_int *n, mp_digit rho) MP_WUR; +MP_PRIVATE mp_err s_mp_mul(const mp_int *a, const mp_int *b, mp_int *c, int digs) MP_WUR; +MP_PRIVATE mp_err s_mp_mul_balance(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; +MP_PRIVATE mp_err s_mp_mul_comba(const mp_int *a, const mp_int *b, mp_int *c, int digs) MP_WUR; +MP_PRIVATE mp_err s_mp_mul_high(const mp_int *a, const mp_int *b, mp_int *c, int digs) MP_WUR; +MP_PRIVATE mp_err s_mp_mul_high_comba(const mp_int *a, const mp_int *b, mp_int *c, int digs) MP_WUR; +MP_PRIVATE mp_err s_mp_mul_karatsuba(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; +MP_PRIVATE mp_err s_mp_mul_toom(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; +MP_PRIVATE mp_err s_mp_prime_is_divisible(const mp_int *a, bool *result) MP_WUR; +MP_PRIVATE mp_err s_mp_rand_platform(void *p, size_t n) MP_WUR; +MP_PRIVATE mp_err s_mp_sqr(const mp_int *a, mp_int *b) MP_WUR; +MP_PRIVATE mp_err s_mp_sqr_comba(const mp_int *a, mp_int *b) MP_WUR; +MP_PRIVATE mp_err s_mp_sqr_karatsuba(const mp_int *a, mp_int *b) MP_WUR; +MP_PRIVATE mp_err s_mp_sqr_toom(const mp_int *a, mp_int *b) MP_WUR; +MP_PRIVATE mp_err s_mp_sub(const mp_int *a, const mp_int *b, mp_int *c) MP_WUR; +MP_PRIVATE void s_mp_copy_digs(mp_digit *d, const mp_digit *s, int digits); +MP_PRIVATE void s_mp_zero_buf(void *mem, size_t size); +MP_PRIVATE void s_mp_zero_digs(mp_digit *d, int digits); +MP_PRIVATE mp_err s_mp_radix_size_overestimate(const mp_int *a, const int radix, size_t *size); + +#define MP_RADIX_MAP_REVERSE_SIZE 80u +extern MP_PRIVATE const char s_mp_radix_map[]; +extern MP_PRIVATE const uint8_t s_mp_radix_map_reverse[]; +extern MP_PRIVATE const mp_digit s_mp_prime_tab[]; + +/* number of primes */ +#define MP_PRIME_TAB_SIZE 256 + +#define MP_GET_ENDIANNESS(x) \ + do{\ + int16_t n = 0x1; \ + char *p = (char *)&n; \ + x = (p[0] == '\x01') ? MP_LITTLE_ENDIAN : MP_BIG_ENDIAN; \ + } while (0) + +/* code-generating macros */ +#define MP_SET_UNSIGNED(name, type) \ + void name(mp_int * a, type b) \ + { \ + int i = 0; \ + while (b != 0u) { \ + a->dp[i++] = ((mp_digit)b & MP_MASK); \ + if (MP_SIZEOF_BITS(type) <= MP_DIGIT_BIT) { break; } \ + b >>= ((MP_SIZEOF_BITS(type) <= MP_DIGIT_BIT) ? 0 : MP_DIGIT_BIT); \ + } \ + a->used = i; \ + a->sign = MP_ZPOS; \ + s_mp_zero_digs(a->dp + a->used, a->alloc - a->used); \ + } + +#define MP_SET_SIGNED(name, uname, type, utype) \ + void name(mp_int * a, type b) \ + { \ + uname(a, (b < 0) ? -(utype)b : (utype)b); \ + if (b < 0) { a->sign = MP_NEG; } \ + } + +#define MP_INIT_INT(name , set, type) \ + mp_err name(mp_int * a, type b) \ + { \ + mp_err err; \ + if ((err = mp_init(a)) != MP_OKAY) { \ + return err; \ + } \ + set(a, b); \ + return MP_OKAY; \ + } + +#define MP_GET_MAG(name, type) \ + type name(const mp_int* a) \ + { \ + int i = MP_MIN(a->used, (int)((MP_SIZEOF_BITS(type) + MP_DIGIT_BIT - 1) / MP_DIGIT_BIT)); \ + type res = 0u; \ + while (i --> 0) { \ + res <<= ((MP_SIZEOF_BITS(type) <= MP_DIGIT_BIT) ? 0 : MP_DIGIT_BIT); \ + res |= (type)a->dp[i]; \ + if (MP_SIZEOF_BITS(type) <= MP_DIGIT_BIT) { break; } \ + } \ + return res; \ + } + +#define MP_GET_SIGNED(name, mag, type, utype) \ + type name(const mp_int* a) \ + { \ + utype res = mag(a); \ + return mp_isneg(a) ? (type)-res : (type)res; \ + } + +#endif diff --git a/src/libtommath/tommath_superclass.h b/src/libtommath/tommath_superclass.h new file mode 100644 index 000000000..9245e0020 --- /dev/null +++ b/src/libtommath/tommath_superclass.h @@ -0,0 +1,113 @@ +/* LibTomMath, multiple-precision integer library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +/* super class file for PK algos */ + +/* default ... include all MPI */ +#ifndef LTM_NOTHING +#define LTM_ALL +#endif + +/* RSA only (does not support DH/DSA/ECC) */ +/* #define SC_RSA_1 */ +/* #define SC_RSA_1_WITH_TESTS */ + +/* For reference.... On an Athlon64 optimizing for speed... + + LTM's mpi.o with all functions [striped] is 142KiB in size. + +*/ + +#ifdef SC_RSA_1_WITH_TESTS +# define MP_ERROR_TO_STRING_C +# define MP_FREAD_C +# define MP_FWRITE_C +# define MP_INCR_C +# define MP_ISEVEN_C +# define MP_ISODD_C +# define MP_NEG_C +# define MP_PRIME_FROBENIUS_UNDERWOOD_C +# define MP_RADIX_SIZE_C +# define MP_RADIX_SIZE_OVERESTIMATE_C +# define MP_LOG_N_C +# define MP_RAND_C +# define MP_REDUCE_C +# define MP_REDUCE_2K_L_C +# define MP_FROM_SBIN_C +# define MP_ROOT_N_C +# define MP_SET_L_C +# define MP_SET_UL_C +# define MP_SET_U64_C +# define MP_SET_I64_C +# define MP_SBIN_SIZE_C +# define MP_TO_RADIX_C +# define MP_TO_SBIN_C +# define S_MP_RAND_JENKINS_C +# define S_MP_RAND_PLATFORM_C +#endif + +/* Works for RSA only, mpi.o is 68KiB */ +#if defined(SC_RSA_1) || defined (SC_RSA_1_WITH_TESTS) +# define MP_CUTOFFS_C +# define MP_ADDMOD_C +# define MP_CLEAR_MULTI_C +# define MP_EXPTMOD_C +# define MP_GCD_C +# define MP_INIT_MULTI_C +# define MP_INVMOD_C +# define MP_LCM_C +# define MP_MOD_C +# define MP_MOD_D_C +# define MP_MULMOD_C +# define MP_PRIME_IS_PRIME_C +# define MP_PRIME_RABIN_MILLER_TRIALS_C +# define MP_PRIME_RAND_C +# define MP_SET_INT_C +# define MP_SHRINK_C +# define MP_TO_UNSIGNED_BIN_C +# define MP_UNSIGNED_BIN_SIZE_C +# define S_MP_PRIME_TAB_C +# define S_MP_RADIX_MAP_C + +/* other modifiers */ + + + +/* here we are on the last pass so we turn things off. The functions classes are still there + * but we remove them specifically from the build. This also invokes tweaks in functions + * like removing support for even moduli, etc... + */ +# ifdef LTM_LAST +# undef MP_DR_IS_MODULUS_C +# undef MP_DR_REDUCE_C +# undef MP_DR_SETUP_C +# undef MP_REDUCE_2K_C +# undef MP_REDUCE_2K_SETUP_C +# undef MP_REDUCE_IS_2K_C +# undef MP_REDUCE_SETUP_C +# undef S_MP_DIV_3_C +# undef S_MP_EXPTMOD_C +# undef S_MP_INVMOD_ODD_C +# undef S_MP_MUL_BALANCE_C +# undef S_MP_MUL_HIGH_C +# undef S_MP_MUL_HIGH_COMBA_C +# undef S_MP_MUL_KARATSUBA_C +# undef S_MP_MUL_TOOM_C +# undef S_MP_SQR_KARATSUBA_C +# undef S_MP_SQR_TOOM_C + +# ifndef SC_RSA_1_WITH_TESTS +# undef MP_REDUCE_C +# endif + +/* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold + * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines] + * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without + * trouble. + */ +# undef MP_MONTGOMERY_REDUCE_C +# undef S_MP_MUL_C +# undef S_MP_SQR_C +# endif + +#endif diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index c351876d4..fa45583d6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -6558,36 +6558,70 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc case ExactValue_Integer: if (is_type_pointer(type)) { - unsigned len = cast(unsigned)value.value_integer.len; LLVMTypeRef t = lb_type(m, original_type); - if (len == 0) { + if (mp_iszero(&value.value_integer)) { res.value = LLVMConstNull(t); } else { - LLVMValueRef i = LLVMConstIntOfArbitraryPrecision(lb_type(m, t_uintptr), len, big_int_ptr(&value.value_integer)); + unsigned len = cast(unsigned)value.value_integer.used; + u64 v = mp_get_u64(&value.value_integer); + LLVMValueRef i = LLVMConstInt(lb_type(m, t_uintptr), cast(unsigned long long)v, false); res.value = LLVMConstIntToPtr(i, t); } } else { - unsigned len = cast(unsigned)value.value_integer.len; - if (len == 0) { + if (mp_iszero(&value.value_integer)) { res.value = LLVMConstNull(lb_type(m, original_type)); } else { - u64 *words = big_int_ptr(&value.value_integer); - if (is_type_different_to_arch_endianness(type)) { - // NOTE(bill): Swap byte order for different endianness - i64 sz = type_size_of(type); - isize byte_len = gb_size_of(u64)*len; - u8 *old_bytes = cast(u8 *)words; - // TODO(bill): Use a different allocator here for a temporary allocation - u8 *new_bytes = cast(u8 *)gb_alloc_align(permanent_allocator(), byte_len, gb_align_of(u64)); - for (i64 i = 0; i < sz; i++) { - new_bytes[i] = old_bytes[sz-1-i]; - } - words = cast(u64 *)new_bytes; + mp_int *a = &value.value_integer; + + u8 *rop = nullptr; + size_t max_count = 0; + size_t written = 0; + size_t size = 1; + size_t nails = 0; + mp_endian endian = MP_NATIVE_ENDIAN; + if (is_type_endian_little(type)) { + endian = MP_LITTLE_ENDIAN; + } else if (is_type_endian_big(type)) { + endian = MP_BIG_ENDIAN; } - res.value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), len, words); - if (value.value_integer.neg) { + + max_count = mp_pack_count(a, nails, size); + rop = cast(u8 *)gb_alloc_align(permanent_allocator(), max_count, gb_align_of(u64)); + mp_err err = mp_pack(rop, max_count, &written, + MP_LSB_FIRST, + size, endian, nails, + &value.value_integer); + GB_ASSERT(err == MP_OKAY); + + res.value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)((written+7)/8), cast(u64 *)rop); + if (value.value_integer.sign) { res.value = LLVMConstNeg(res.value); } + + // size_t written = 0; + // size_t max_bytes = (value.value_integer.used*gb_size_of(mp_digit)+7)&~7; + // u8 *buf = cast(u8 *)gb_alloc_align(permanent_allocator(), max_bytes, gb_align_of(u64)); + // mp_to_ubin(&value.value_integer, buf, max_bytes, &written); + + // gb_printf_err("%tu %tu", written, max_bytes); + // for (size_t i = 0; i < written; i++) { + // gb_printf_err("%02x", buf[i]); + // } + // gb_printf_err("\n"); + // gb_exit(1); + + // if (is_type_different_to_arch_endianness(type)) { + // u8 *old_bytes = buf; + // u8 *new_bytes = cast(u8 *)gb_alloc_align(permanent_allocator(), max_bytes, gb_align_of(u64)); + // for (size_t i = 0; i < written; i++) { + // new_bytes[i] = old_bytes[written-1-i]; + // } + // buf = new_bytes; + // } + // res.value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)((written+7)/8), cast(u64 *)buf); + // if (value.value_integer.sign) { + // res.value = LLVMConstNeg(res.value); + // } } } return res; diff --git a/src/main.cpp b/src/main.cpp index 345642cc6..8c4eb7d7c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1970,7 +1970,6 @@ int main(int arg_count, char const **arg_ptr) { init_string_interner(); init_global_error_collector(); init_keyword_hash_table(); - global_big_int_init(); init_type_mutex(); if (!check_env()) {