#include "includes.h"
#include "moduli.h"
#include "xmalloc.h"
#include "log.h"
#include <openssl/bn.h>
#define QLINESIZE (100+8192)
#define QTYPE_UNKNOWN (0)
#define QTYPE_UNSTRUCTURED (1)
#define QTYPE_SAFE (2)
#define QTYPE_SCHNOOR (3)
#define QTYPE_SOPHIE_GERMAINE (4)
#define QTYPE_STRONG (5)
#define QTEST_UNTESTED (0x00)
#define QTEST_COMPOSITE (0x01)
#define QTEST_SIEVE (0x02)
#define QTEST_MILLER_RABIN (0x04)
#define QTEST_JACOBI (0x08)
#define QTEST_ELLIPTIC (0x10)
#define QSIZE_MINIMUM (511)
#define SHIFT_BIT (3)
#define SHIFT_BYTE (2)
#define SHIFT_WORD (SHIFT_BIT+SHIFT_BYTE)
#define SHIFT_MEGABYTE (20)
#define SHIFT_MEGAWORD (SHIFT_MEGABYTE-SHIFT_BYTE)
#define SMALL_MAXIMUM (0xffffffffUL)
#define TINY_NUMBER (1UL<<16)
#define TEST_MAXIMUM (1UL<<16)
#define TEST_MINIMUM (QSIZE_MINIMUM + 1)
#define TEST_POWER (3)
#define BIT_CLEAR(a,n) ((a)[(n)>>SHIFT_WORD] &= ~(1L << ((n) & 31)))
#define BIT_SET(a,n) ((a)[(n)>>SHIFT_WORD] |= (1L << ((n) & 31)))
#define BIT_TEST(a,n) ((a)[(n)>>SHIFT_WORD] & (1L << ((n) & 31)))
static u_int32_t *TinySieve, tinybits;
static u_int32_t *SmallSieve, smallbits, smallbase;
static u_int32_t *LargeSieve, largewords, largetries, largenumbers;
static u_int32_t largebits, largememory;
static BIGNUM *largebase;
static int
qfileout(FILE * ofile, u_int32_t otype, u_int32_t otests, u_int32_t otries,
u_int32_t osize, u_int32_t ogenerator, BIGNUM * omodulus)
{
struct tm *gtm;
time_t time_now;
int res;
time(&time_now);
gtm = gmtime(&time_now);
res = fprintf(ofile, "%04d%02d%02d%02d%02d%02d %u %u %u %u %x ",
gtm->tm_year + 1900, gtm->tm_mon + 1, gtm->tm_mday,
gtm->tm_hour, gtm->tm_min, gtm->tm_sec,
otype, otests, otries, osize, ogenerator);
if (res < 0)
return (-1);
if (BN_print_fp(ofile, omodulus) < 1)
return (-1);
res = fprintf(ofile, "\n");
fflush(ofile);
return (res > 0 ? 0 : -1);
}
static void
sieve_large(u_int32_t s)
{
u_int32_t r, u;
debug3("sieve_large %u", s);
largetries++;
r = BN_mod_word(largebase, s);
if (r == 0)
u = 0;
else
u = s - r;
if (u < largebits * 2) {
if (u & 0x1)
u += s;
for (u /= 2; u < largebits; u += s)
BIT_SET(LargeSieve, u);
}
r = (2 * r + 1) % s;
if (r == 0)
u = 0;
else
u = s - r;
if (u < largebits * 4) {
while (u & 0x3) {
if (SMALL_MAXIMUM - u < s)
return;
u += s;
}
for (u /= 4; u < largebits; u += s)
BIT_SET(LargeSieve, u);
}
}
int
gen_candidates(FILE *out, int memory, int power, BIGNUM *start)
{
BIGNUM *q;
u_int32_t j, r, s, t;
u_int32_t smallwords = TINY_NUMBER >> 6;
u_int32_t tinywords = TINY_NUMBER >> 6;
time_t time_start, time_stop;
int i, ret = 0;
largememory = memory;
if (power > TEST_MAXIMUM) {
error("Too many bits: %u > %lu", power, TEST_MAXIMUM);
return (-1);
} else if (power < TEST_MINIMUM) {
error("Too few bits: %u < %u", power, TEST_MINIMUM);
return (-1);
}
power--;
largewords = ((power * power) >> (SHIFT_WORD - TEST_POWER));
if (largememory > LARGE_MAXIMUM) {
logit("Limited memory: %u MB; limit %lu MB",
largememory, LARGE_MAXIMUM);
largememory = LARGE_MAXIMUM;
}
if (largewords <= (largememory << SHIFT_MEGAWORD)) {
logit("Increased memory: %u MB; need %u bytes",
largememory, (largewords << SHIFT_BYTE));
largewords = (largememory << SHIFT_MEGAWORD);
} else if (largememory > 0) {
logit("Decreased memory: %u MB; want %u bytes",
largememory, (largewords << SHIFT_BYTE));
largewords = (largememory << SHIFT_MEGAWORD);
}
TinySieve = calloc(tinywords, sizeof(u_int32_t));
if (TinySieve == NULL) {
error("Insufficient memory for tiny sieve: need %u bytes",
tinywords << SHIFT_BYTE);
exit(1);
}
tinybits = tinywords << SHIFT_WORD;
SmallSieve = calloc(smallwords, sizeof(u_int32_t));
if (SmallSieve == NULL) {
error("Insufficient memory for small sieve: need %u bytes",
smallwords << SHIFT_BYTE);
xfree(TinySieve);
exit(1);
}
smallbits = smallwords << SHIFT_WORD;
while ((LargeSieve = calloc(largewords, sizeof(u_int32_t))) == NULL)
largewords -= (1L << (SHIFT_MEGAWORD - 2));
largebits = largewords << SHIFT_WORD;
largenumbers = largebits * 2;
largetries = 0;
q = BN_new();
largebase = BN_new();
if (start == NULL)
BN_rand(largebase, power, 1, 1);
else
BN_copy(largebase, start);
BN_set_bit(largebase, 0);
time(&time_start);
logit("%.24s Sieve next %u plus %u-bit", ctime(&time_start),
largenumbers, power);
debug2("start point: 0x%s", BN_bn2hex(largebase));
for (i = 0; i < tinybits; i++) {
if (BIT_TEST(TinySieve, i))
continue;
t = 2 * i + 3;
for (j = i + t; j < tinybits; j += t)
BIT_SET(TinySieve, j);
sieve_large(t);
}
for (smallbase = TINY_NUMBER + 3;
smallbase < (SMALL_MAXIMUM - TINY_NUMBER);
smallbase += TINY_NUMBER) {
for (i = 0; i < tinybits; i++) {
if (BIT_TEST(TinySieve, i))
continue;
t = 2 * i + 3;
r = smallbase % t;
if (r == 0) {
s = 0;
} else {
s = t - r;
}
if (s & 1)
s += t;
for (s /= 2; s < smallbits; s += t)
BIT_SET(SmallSieve, s);
}
for (i = 0; i < smallbits; i++) {
if (BIT_TEST(SmallSieve, i))
continue;
sieve_large((2 * i) + smallbase);
}
memset(SmallSieve, 0, smallwords << SHIFT_BYTE);
}
time(&time_stop);
logit("%.24s Sieved with %u small primes in %ld seconds",
ctime(&time_stop), largetries, (long) (time_stop - time_start));
for (j = r = 0; j < largebits; j++) {
if (BIT_TEST(LargeSieve, j))
continue;
debug2("test q = largebase+%u", 2 * j);
BN_set_word(q, 2 * j);
BN_add(q, q, largebase);
if (qfileout(out, QTYPE_SOPHIE_GERMAINE, QTEST_SIEVE,
largetries, (power - 1) , (0), q) == -1) {
ret = -1;
break;
}
r++;
}
time(&time_stop);
xfree(LargeSieve);
xfree(SmallSieve);
xfree(TinySieve);
logit("%.24s Found %u candidates", ctime(&time_stop), r);
return (ret);
}
int
prime_test(FILE *in, FILE *out, u_int32_t trials,
u_int32_t generator_wanted)
{
BIGNUM *q, *p, *a;
BN_CTX *ctx;
char *cp, *lp;
u_int32_t count_in = 0, count_out = 0, count_possible = 0;
u_int32_t generator_known, in_tests, in_tries, in_type, in_size;
time_t time_start, time_stop;
int res;
time(&time_start);
p = BN_new();
q = BN_new();
ctx = BN_CTX_new();
debug2("%.24s Final %u Miller-Rabin trials (%x generator)",
ctime(&time_start), trials, generator_wanted);
res = 0;
lp = xmalloc(QLINESIZE + 1);
while (fgets(lp, QLINESIZE, in) != NULL) {
int ll = strlen(lp);
count_in++;
if (ll < 14 || *lp == '!' || *lp == '#') {
debug2("%10u: comment or short line", count_in);
continue;
}
cp = &lp[14];
in_type = strtoul(cp, &cp, 10);
in_tests = strtoul(cp, &cp, 10);
if (in_tests & QTEST_COMPOSITE) {
debug2("%10u: known composite", count_in);
continue;
}
in_tries = strtoul(cp, &cp, 10);
in_size = strtoul(cp, &cp, 10);
generator_known = strtoul(cp, &cp, 16);
cp += strspn(cp, " ");
switch (in_type) {
case QTYPE_SOPHIE_GERMAINE:
debug2("%10u: (%u) Sophie-Germaine", count_in, in_type);
a = q;
BN_hex2bn(&a, cp);
BN_lshift(p, q, 1);
BN_add_word(p, 1);
in_size += 1;
generator_known = 0;
break;
case QTYPE_UNSTRUCTURED:
case QTYPE_SAFE:
case QTYPE_SCHNOOR:
case QTYPE_STRONG:
case QTYPE_UNKNOWN:
debug2("%10u: (%u)", count_in, in_type);
a = p;
BN_hex2bn(&a, cp);
BN_rshift(q, p, 1);
break;
default:
debug2("Unknown prime type");
break;
}
if (BN_num_bits(p) != (in_size + 1)) {
debug2("%10u: bit size %u mismatch", count_in, in_size);
continue;
}
if (in_size < QSIZE_MINIMUM) {
debug2("%10u: bit size %u too short", count_in, in_size);
continue;
}
if (in_tests & QTEST_MILLER_RABIN)
in_tries += trials;
else
in_tries = trials;
if (generator_known == 0) {
if (BN_mod_word(p, 24) == 11)
generator_known = 2;
else if (BN_mod_word(p, 12) == 5)
generator_known = 3;
else {
u_int32_t r = BN_mod_word(p, 10);
if (r == 3 || r == 7)
generator_known = 5;
}
}
if (generator_wanted > 0 &&
generator_wanted != generator_known) {
debug2("%10u: generator %d != %d",
count_in, generator_known, generator_wanted);
continue;
}
if (generator_known == 0) {
debug2("%10u: no known generator", count_in);
continue;
}
count_possible++;
if (BN_is_prime(q, 1, NULL, ctx, NULL) <= 0) {
debug("%10u: q failed first possible prime test",
count_in);
continue;
}
if (!BN_is_prime(p, trials, NULL, ctx, NULL)) {
debug("%10u: p is not prime", count_in);
continue;
}
debug("%10u: p is almost certainly prime", count_in);
if (!BN_is_prime(q, trials - 1, NULL, ctx, NULL)) {
debug("%10u: q is not prime", count_in);
continue;
}
debug("%10u: q is almost certainly prime", count_in);
if (qfileout(out, QTYPE_SAFE, (in_tests | QTEST_MILLER_RABIN),
in_tries, in_size, generator_known, p)) {
res = -1;
break;
}
count_out++;
}
time(&time_stop);
xfree(lp);
BN_free(p);
BN_free(q);
BN_CTX_free(ctx);
logit("%.24s Found %u safe primes of %u candidates in %ld seconds",
ctime(&time_stop), count_out, count_possible,
(long) (time_stop - time_start));
return (res);
}