#include <openssl/bn.h>
#include "bn_local.h"
#include "bn_prime.h"
static int
bn_div_by_two_mod_odd_n(BIGNUM *a, const BIGNUM *n, BN_CTX *ctx)
{
if (!BN_is_odd(n))
return 0;
if (BN_is_odd(a)) {
if (!BN_add(a, a, n))
return 0;
}
if (!BN_rshift1(a, a))
return 0;
if (!BN_mod_ct(a, a, n, ctx))
return 0;
return 1;
}
static int
bn_lucas_step(BIGNUM *U, BIGNUM *V, int digit, const BIGNUM *D,
const BIGNUM *n, BN_CTX *ctx)
{
BIGNUM *tmp;
int ret = 0;
BN_CTX_start(ctx);
if ((tmp = BN_CTX_get(ctx)) == NULL)
goto err;
if (!BN_sqr(tmp, U, ctx))
goto err;
if (!BN_mul(tmp, D, tmp, ctx))
goto err;
if (!BN_mod_mul(U, U, V, n, ctx))
goto err;
if (!BN_sqr(V, V, ctx))
goto err;
if (!BN_add(V, V, tmp))
goto err;
if (!bn_div_by_two_mod_odd_n(V, n, ctx))
goto err;
if (digit == 1) {
if (!BN_mul(tmp, D, U, ctx))
goto err;
if (!BN_add(U, U, V))
goto err;
if (!bn_div_by_two_mod_odd_n(U, n, ctx))
goto err;
if (!BN_add(V, V, tmp))
goto err;
if (!bn_div_by_two_mod_odd_n(V, n, ctx))
goto err;
}
ret = 1;
err:
BN_CTX_end(ctx);
return ret;
}
static int
bn_lucas(BIGNUM *U, BIGNUM *V, const BIGNUM *k, const BIGNUM *D,
const BIGNUM *n, BN_CTX *ctx)
{
int digit, i;
int ret = 0;
if (!BN_one(U))
goto err;
if (!BN_one(V))
goto err;
for (i = BN_num_bits(k) - 2; i >= 0; i--) {
digit = BN_is_bit_set(k, i);
if (!bn_lucas_step(U, V, digit, D, n, ctx))
goto err;
}
ret = 1;
err:
return ret;
}
static int
bn_strong_lucas_test(int *is_pseudoprime, const BIGNUM *n, const BIGNUM *D,
BN_CTX *ctx)
{
BIGNUM *k, *U, *V;
int r, s;
int ret = 0;
BN_CTX_start(ctx);
if ((k = BN_CTX_get(ctx)) == NULL)
goto err;
if ((U = BN_CTX_get(ctx)) == NULL)
goto err;
if ((V = BN_CTX_get(ctx)) == NULL)
goto err;
s = 0;
while (BN_is_bit_set(n, s))
s++;
if (!BN_rshift(k, n, s))
goto err;
if (!BN_set_bit(k, 0))
goto err;
if (!bn_lucas(U, V, k, D, n, ctx))
goto err;
if (BN_is_zero(U) || BN_is_zero(V)) {
*is_pseudoprime = 1;
goto done;
}
for (r = 1; r < s; r++) {
if (!bn_lucas_step(U, V, 0, D, n, ctx))
goto err;
if (BN_is_zero(V)) {
*is_pseudoprime = 1;
goto done;
}
}
*is_pseudoprime = 0;
done:
ret = 1;
err:
BN_CTX_end(ctx);
return ret;
}
static int
bn_strong_lucas_selfridge(int *is_pseudoprime, const BIGNUM *n, BN_CTX *ctx)
{
BIGNUM *D, *two;
int is_perfect_square, jacobi_symbol, sign;
int ret = 0;
BN_CTX_start(ctx);
if (!bn_is_perfect_square(&is_perfect_square, n, ctx))
goto err;
if (is_perfect_square) {
*is_pseudoprime = 0;
goto done;
}
if ((D = BN_CTX_get(ctx)) == NULL)
goto err;
if ((two = BN_CTX_get(ctx)) == NULL)
goto err;
sign = 1;
if (!BN_set_word(D, 5))
goto err;
if (!BN_set_word(two, 2))
goto err;
while (1) {
if ((jacobi_symbol = BN_kronecker(D, n, ctx)) == -2)
goto err;
if (jacobi_symbol == -1)
break;
if (jacobi_symbol == 0) {
*is_pseudoprime = 0;
goto done;
}
sign = -sign;
if (!BN_uadd(D, D, two))
goto err;
BN_set_negative(D, sign == -1);
}
if (!bn_strong_lucas_test(is_pseudoprime, n, D, ctx))
goto err;
done:
ret = 1;
err:
BN_CTX_end(ctx);
return ret;
}
static int
bn_fermat(int *is_pseudoprime, const BIGNUM *n, const BIGNUM *n_minus_one,
const BIGNUM *k, int s, const BIGNUM *base, BN_CTX *ctx, BN_MONT_CTX *mctx)
{
BIGNUM *power;
int ret = 0;
int i;
BN_CTX_start(ctx);
if ((power = BN_CTX_get(ctx)) == NULL)
goto err;
if (BN_cmp(base, BN_value_one()) <= 0 || BN_cmp(base, n_minus_one) >= 0)
goto err;
if (!BN_mod_exp_mont_ct(power, base, k, n, ctx, mctx))
goto err;
if (BN_is_one(power) || BN_cmp(power, n_minus_one) == 0) {
*is_pseudoprime = 1;
goto done;
}
for (i = 1; i < s; i++) {
if (!BN_mod_sqr(power, power, n, ctx))
goto err;
if (BN_cmp(power, n_minus_one) == 0) {
*is_pseudoprime = 1;
goto done;
}
if (BN_is_one(power)) {
*is_pseudoprime = 0;
goto done;
}
}
*is_pseudoprime = 0;
done:
ret = 1;
err:
BN_CTX_end(ctx);
return ret;
}
static int
bn_miller_rabin(int *is_pseudoprime, const BIGNUM *n, BN_CTX *ctx,
size_t rounds)
{
BN_MONT_CTX *mctx = NULL;
BIGNUM *base, *k, *n_minus_one;
size_t i;
int s;
int ret = 0;
BN_CTX_start(ctx);
if ((base = BN_CTX_get(ctx)) == NULL)
goto err;
if ((k = BN_CTX_get(ctx)) == NULL)
goto err;
if ((n_minus_one = BN_CTX_get(ctx)) == NULL)
goto err;
if (BN_is_word(n, 2) || BN_is_word(n, 3)) {
*is_pseudoprime = 1;
goto done;
}
if (BN_cmp(n, BN_value_one()) <= 0 || !BN_is_odd(n)) {
*is_pseudoprime = 0;
goto done;
}
if (!BN_sub(n_minus_one, n, BN_value_one()))
goto err;
s = 0;
while (!BN_is_bit_set(n_minus_one, s))
s++;
if (!BN_rshift(k, n_minus_one, s))
goto err;
if ((mctx = BN_MONT_CTX_create(n, ctx)) == NULL)
goto err;
if (!BN_set_word(base, 2))
goto err;
if (!bn_fermat(is_pseudoprime, n, n_minus_one, k, s, base, ctx, mctx))
goto err;
if (!*is_pseudoprime)
goto done;
for (i = 0; i < rounds; i++) {
if (!bn_rand_interval(base, 3, n_minus_one))
goto err;
if (!bn_fermat(is_pseudoprime, n, n_minus_one, k, s, base, ctx,
mctx))
goto err;
if (!*is_pseudoprime)
goto done;
}
*is_pseudoprime = 1;
done:
ret = 1;
err:
BN_MONT_CTX_free(mctx);
BN_CTX_end(ctx);
return ret;
}
int
bn_is_prime_bpsw(int *is_pseudoprime, const BIGNUM *n, BN_CTX *in_ctx,
size_t rounds)
{
BN_CTX *ctx = NULL;
BN_ULONG mod;
int i;
int ret = 0;
if (BN_is_word(n, 2)) {
*is_pseudoprime = 1;
goto done;
}
if (BN_cmp(n, BN_value_one()) <= 0 || !BN_is_odd(n)) {
*is_pseudoprime = 0;
goto done;
}
for (i = 0; i < NUMPRIMES; i++) {
if ((mod = BN_mod_word(n, primes[i])) == (BN_ULONG)-1)
goto err;
if (mod == 0) {
*is_pseudoprime = BN_is_word(n, primes[i]);
goto done;
}
}
if ((ctx = in_ctx) == NULL)
ctx = BN_CTX_new();
if (ctx == NULL)
goto err;
if (!bn_miller_rabin(is_pseudoprime, n, ctx, rounds))
goto err;
if (!*is_pseudoprime)
goto done;
if (!bn_strong_lucas_selfridge(is_pseudoprime, n, ctx))
goto err;
done:
ret = 1;
err:
if (ctx != in_ctx)
BN_CTX_free(ctx);
return ret;
}