diff options
Diffstat (limited to 'lib/libmpa/mpa_primetest.c')
-rw-r--r-- | lib/libmpa/mpa_primetest.c | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/lib/libmpa/mpa_primetest.c b/lib/libmpa/mpa_primetest.c new file mode 100644 index 0000000..f8d67fa --- /dev/null +++ b/lib/libmpa/mpa_primetest.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2014, STMicroelectronics International N.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "mpa.h" + +#define USE_PRIME_TABLE + +#if defined(USE_PRIME_TABLE) +#include "mpa_primetable.h" +#endif + +#define DEF_COMPOSITE 0 +#define DEF_PRIME 1 +#define PROB_PRIME -1 + +/* Product of all primes < 1000 */ +static const mpa_num_base const_small_prime_factors = { + 44, + 44, + {0x2ED42696, 0x2BBFA177, 0x4820594F, 0xF73F4841, + 0xBFAC313A, 0xCAC3EB81, 0xF6F26BF8, 0x7FAB5061, + 0x59746FB7, 0xF71377F6, 0x3B19855B, 0xCBD03132, + 0xBB92EF1B, 0x3AC3152C, 0xE87C8273, 0xC0AE0E69, + 0x74A9E295, 0x448CCE86, 0x63CA1907, 0x8A0BF944, + 0xF8CC3BE0, 0xC26F0AF5, 0xC501C02F, 0x6579441A, + 0xD1099CDA, 0x6BC76A00, 0xC81A3228, 0xBFB1AB25, + 0x70FA3841, 0x51B3D076, 0xCC2359ED, 0xD9EE0769, + 0x75E47AF0, 0xD45FF31E, 0x52CCE4F6, 0x04DBC891, + 0x96658ED2, 0x1753EFE5, 0x3AE4A5A6, 0x8FD4A97F, + 0x8B15E7EB, 0x0243C3E1, 0xE0F0C31D, 0x0000000B} +}; + +/* + * If n is less than this number (341550071728321 decimal) the Miller-Rabin + * test (using specific bases) constitutes a primality proof. + */ +static const mpa_num_base const_miller_rabin_proof_limit = { + 2, + 2, + {0x52B2C8C1, 0x000136A3} +}; + +static const mpa_num_base const_two = { + 1, + 1, + {0x00000002} +}; + +/* foward declarations */ +static int is_small_prime(mpanum n); +static int has_small_factors(mpanum n, mpa_scratch_mem pool); +static int primality_test_miller_rabin(mpanum n, int conf_level, + mpa_scratch_mem pool); + +/*------------------------------------------------------------ + * + * mpa_is_prob_prime + * + * Returns: + * 0 if n is definitely composite + * 1 if n is definitely prime + * -1 if n is composite with a probability less than 2^(-conf_level) + * + */ +int mpa_is_prob_prime(mpanum n, int conf_level, mpa_scratch_mem pool) +{ + int result = 0; + + /* Check if it's a small prime */ + result = is_small_prime(n); + if (result != PROB_PRIME) + goto cleanup; + + /* Test if n is divisible by any prime < 1000 */ + if (has_small_factors(n, pool)) { + result = DEF_COMPOSITE; + goto cleanup; + } + /* Check with Miller Rabin */ + result = primality_test_miller_rabin(n, conf_level, pool); + +cleanup: + return result; +} + +#if defined(USE_PRIME_TABLE) +/*------------------------------------------------------------ + * + * check_table + * + */ +static uint32_t check_table(uint32_t v) +{ + return (PRIME_TABLE[v >> 5] >> (v & 0x1f)) & 1; +} +#endif + +/*------------------------------------------------------------ + * + * is_small_prime + * + * Returns 1 if n is prime, + Returns 0 if n is composite + * Returns -1 if we cannot decide + * + */ +static int is_small_prime(mpanum n) +{ + mpa_word_t v; + + /* If n is larger than a mpa_word_t, we can only decide if */ + /* n is even. If it's odd we cannot tell. */ + if (__mpanum_size(n) > 1) + return ((mpa_parity(n) == MPA_EVEN_PARITY) ? 0 : -1); + + v = mpa_get_word(n); /* will convert negative n:s to positive v:s. */ + if ((v | 1) == 1) /* 0 and 1 are not prime */ + return DEF_COMPOSITE; + if (v == 2) /* 2 is prime */ + return DEF_PRIME; + if ((v & 1) == 0) + return DEF_COMPOSITE; /* but no other even number */ + +#if defined(USE_PRIME_TABLE) + if (mpa_cmp_short(n, MAX_TABULATED_PRIME) > 0) + return -1; + v = (v - 3) >> 1; + return check_table(v); +#else + return -1; +#endif +} + +/*------------------------------------------------------------ + * + * has_small_factors + * + * returns 1 if n has small factors + * returns 0 if not. + */ +static int has_small_factors(mpanum n, mpa_scratch_mem pool) +{ + const mpa_num_base *factors = &const_small_prime_factors; + int result; + mpanum res; + + mpa_alloc_static_temp_var(&res, pool); + mpa_gcd(res, n, (const mpanum)factors, pool); + result = (mpa_cmp_short(res, 1) == 0) ? 0 : 1; + mpa_free_static_temp_var(&res, pool); + + return result; +} + +/*------------------------------------------------------------ + * + * primality_test_miller_rabin + * + */ +static int primality_test_miller_rabin(mpanum n, int conf_level, + mpa_scratch_mem pool) +{ + int result; + bool proof_version; + static const int32_t proof_a[7] = { 2, 3, 5, 7, 11, 13, 17 }; + int cnt; + int idx; + int t; + int e = 0; + int cmp_one; + mpanum a; + mpanum q; + mpanum n_minus_1; + mpanum b; + mpanum r_modn; + mpanum r2_modn; + mpa_word_t n_inv; + + mpa_alloc_static_temp_var(&r_modn, pool); + mpa_alloc_static_temp_var(&r2_modn, pool); + + if (mpa_compute_fmm_context(n, r_modn, r2_modn, &n_inv, pool) == -1) { + result = DEF_COMPOSITE; + goto cleanup_short; + } + + mpa_alloc_static_temp_var(&a, pool); + mpa_alloc_static_temp_var(&q, pool); + mpa_alloc_static_temp_var(&n_minus_1, pool); + mpa_alloc_static_temp_var(&b, pool); + + proof_version = + (mpa_cmp(n, (mpanum) &const_miller_rabin_proof_limit) < 0); + + if (proof_version) + cnt = 7; + else /* MR has 1/4 chance in failing a composite */ + cnt = (conf_level + 1) / 2; + + mpa_sub_word(n_minus_1, n, 1, pool); + mpa_set(q, n_minus_1); + t = 0; + /* calculate q such that n - 1 = 2^t * q where q is odd */ + while (mpa_is_even(q)) { + mpa_shift_right(q, q, 1); + t++; + } + + result = PROB_PRIME; + for (idx = 0; idx < cnt && result == PROB_PRIME; idx++) { + if (proof_version) { + mpa_set_S32(a, proof_a[idx]); + if (mpa_cmp(n, a) == 0) { + result = DEF_PRIME; + continue; + } + } else { + /* + * Get random a, 1 < a < N by + * asking for a random in range 0 <= x < N - 2 + * and then add 2 to it. + */ + mpa_sub_word(n_minus_1, n_minus_1, 1, pool); + /* n_minus_1 is now N - 2 ! */ + mpa_get_random(a, n_minus_1); + mpa_add_word(n_minus_1, n_minus_1, 1, pool); + /* and a is now 2 <= a < N */ + mpa_add_word(a, a, 2, pool); + } + + mpa_exp_mod(b, a, q, n, r_modn, r2_modn, n_inv, pool); + e = 0; + +inner_loop: + cmp_one = mpa_cmp_short(b, 1); + if ((cmp_one == 0) && (e > 0)) { + result = DEF_COMPOSITE; + continue; + } + + if ((mpa_cmp(b, n_minus_1) == 0) || + ((cmp_one == 0) && (e == 0))) { + /* probably prime, try another a */ + continue; + } + + e++; + if (e < t) { + mpa_exp_mod(b, b, (mpanum) &const_two, n, r_modn, + r2_modn, n_inv, pool); + goto inner_loop; + } + result = DEF_COMPOSITE; + } + + if (result == PROB_PRIME && proof_version) + result = DEF_PRIME; + + mpa_free_static_temp_var(&a, pool); + mpa_free_static_temp_var(&q, pool); + mpa_free_static_temp_var(&n_minus_1, pool); + mpa_free_static_temp_var(&b, pool); +cleanup_short: + mpa_free_static_temp_var(&r_modn, pool); + mpa_free_static_temp_var(&r2_modn, pool); + + return result; +} |