#include "os.h"
#include <mp.h>
#include <libsec.h>

/* NIST algorithm for generating DSA primes */
/* Menezes et al (1997) Handbook of Applied Cryptography, p.151 */
/* q is a 160-bit prime;  p is a 1024-bit prime;  q divides p-1 */

/* arithmetic on unsigned ints mod 2**160, represented */
/*    as 20-byte, little-endian uchar array */

static void
Hrand(uchar *s)
{
	uint32 *u = (uint32*)s;
	*u++ = fastrand();
	*u++ = fastrand();
	*u++ = fastrand();
	*u++ = fastrand();
	*u = fastrand();
}

static void
Hincr(uchar *s)
{
	int i;
	for(i=0; i<20; i++)
		if(++s[i]!=0)
			break;
}

/* this can run for quite a while;  be patient */
void
DSAprimes(mpint *q, mpint *p, uchar seed[SHA1dlen])
{
	int i, j, k, n = 6, b = 63;
	uchar s[SHA1dlen], Hs[SHA1dlen], Hs1[SHA1dlen], sj[SHA1dlen], sjk[SHA1dlen];
	mpint *two1023, *mb, *Vk, *W, *X, *q2;

	two1023 = mpnew(1024);
	mpleft(mpone, 1023, two1023);
	mb = mpnew(0);
	mpleft(mpone, b, mb);
	W = mpnew(1024);
	Vk = mpnew(1024);
	X = mpnew(0);
	q2 = mpnew(0);
forever:
	do{
		Hrand(s);
		memcpy(sj, s, 20);
		sha1(s, 20, Hs, 0);
		Hincr(sj);
		sha1(sj, 20, Hs1, 0);
		for(i=0; i<20; i++)
			Hs[i] ^= Hs1[i];
		Hs[0] |= 1;
		Hs[19] |= 0x80;
		letomp(Hs, 20, q);
	}while(!probably_prime(q, 18));
	if(seed != nil)	/* allow skeptics to confirm computation */
		memmove(seed, s, SHA1dlen);
	i = 0;
	j = 2;
	Hincr(sj);
	mpleft(q, 1, q2);
	while(i<4096){
		memcpy(sjk, sj, 20);
		for(k=0; k <= n; k++){
			sha1(sjk, 20, Hs, 0);
			letomp(Hs, 20, Vk);
			if(k == n)
				mpmod(Vk, mb, Vk);
			mpleft(Vk, 160*k, Vk);
			mpadd(W, Vk, W);
			Hincr(sjk);
		}
		mpadd(W, two1023, X);
		mpmod(X, q2, W);
		mpsub(W, mpone, W);
		mpsub(X, W, p);
		if(mpcmp(p, two1023)>=0 && probably_prime(p, 5))
			goto done;
		i += 1;
		j += n+1;
		for(k=0; k<n+1; k++)
			Hincr(sj);
	}
	goto forever;
done:
	mpfree(q2);
	mpfree(X);
	mpfree(Vk);
	mpfree(W);
	mpfree(mb);
	mpfree(two1023);
}
