
/* $Id: bbs.c,v 1.2 2004/06/29 15:36:50 roo2b Exp $ */
/*
 *Copyright (c) 2004 Matthias Bauer <matthiasb@acm.org>
 *
 *Permission is hereby granted, free of charge, to any person obtaining a cop
 *
 *of this software and associated documentation files (the "Software"), to
 *deal in the Software without restriction, including without limitation the
 *rights to use, copy, modify, merge, publish, distribute, sublicense,
 *and/or sell copies of the Software, and to permit persons to whom the
 *Software is furnished to do so, subject to the following conditions:
 *
 *The above copyright notice and this permission notice shall be included in
 *all copies or substantial portions of the Software.
 *
 *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 OR COPYRIGHT HOLDERS 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.
 */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <gmp.h>
#include <openssl/bn.h>   /* Bessere Primtests */

/* Compile with 
 * 
 * gcc -I/usr/local/include -L/usr/local/lib -o bbs bbs.c -lcrypto -lgmp
 *
 * On Linux you must define -DDEVRAND="/dev/random" (untested)
 */

#define PRIMEFILE "blummod"
#define SEEDFILE "bbsseed"
#ifndef DEVRAND
#define DEVRAND  "/dev/arandom"
#endif

extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;
extern int optreset;

FILE *conf, *seed;
mpz_t i,j,n;
mpz_t state, tmpstate;
int foo, rfd, gflag,sflag, pflag, ch, intflag, statesize, nbytes;
FILE *pfd, *sfd;
char sfile[FILENAME_MAX] , pfile[FILENAME_MAX];
int bitsize = 512;

int main(int argc, char** argv) {
	char *check;

	intflag = 0;

	while ((ch = getopt(argc, argv, "b:hgs:m:n:")) != -1) {
		switch(ch){

			case 'g':
				gflag = 1;
				break;
			case 's':
				strlcpy(sfile, optarg, FILENAME_MAX );
				sflag = 1;
				break;
			case 'm':
				strlcpy(pfile, optarg, FILENAME_MAX );
				pflag = 1;
				break;
  			case 'n':
  				nbytes = strtol(optarg, &check, 10);
  				if (optarg[0] == '\0' || *check != '\0') {
  					fprintf(stderr, "Please give number of random bytes to the -n option\n");
  					exit(1);
  				}
				break;
			case 'b':
				bitsize = atoi(optarg);
				break;
			case 'h':
			default:
				(void)usage();
		}
	}
	if (!sflag) {
		strlcpy (sfile, SEEDFILE, 64);
	}

	if (!pflag) {
		strlcpy (pfile, PRIMEFILE, 64);
	}

	if (gflag) {
		char randini[128];
		int i;

		if(( pfd = fopen (pfile, "w+")) == NULL) {
			fprintf(stderr, "Error opening %s\n", pfile);
			exit(1);
		}

		(void) gene_primes(pfd);

		fclose (pfd);
		if(( rfd = open (DEVRAND, O_RDONLY, 0)) < 0) {
			fprintf(stderr, "Could not open %s", DEVRAND);
			exit(1);
		}
		if (read(rfd, randini, 128) < 128) {
			fprintf(stderr, "Could not get 128 bytes from %s\n",DEVRAND);
			exit(1);
		}
		close (rfd);
		if(( sfd = fopen (sfile, "w+")) == NULL) {
			fprintf(stderr, "Error opening %s\n", sfile);
			exit(1);
		}
		for(i=0; i< 128; i++) {
			fprintf(sfd, "%.2x", randini[i]);
		}
		fprintf(sfd, "\n");
		fclose (sfd);
	}

	if(!(conf = fopen (pfile, "r"))) {
		fprintf(stderr, "Could not open %s", pfile);
		exit(1);
	}

	if(!(seed = fopen (SEEDFILE, "r+"))) {
		fprintf(stderr, "Could not open SEEDFILE");
		exit(1);
	}

	mpz_init(i);
	mpz_init(j);
	mpz_init(n);
	mpz_init(state);
	mpz_init(tmpstate);
	
	mpz_set_ui(n,0);
	mpz_inp_str(n, conf, 10);
	fclose (conf);
	if(mpz_cmp_ui(n,0) ==0) {
		fprintf(stderr, "Could not init modulus from %s\n", pfile);
		exit(1);
	}
/*	printf("Modulus: \n");  */
/* 	Mpz_out_str (stdout, 10, n);  */
	printf ("\n");
	mpz_set_ui(state,0);
	mpz_inp_str(state,seed,16);
	if(mpz_cmp_ui(state,0) ==0) {
		fprintf(stderr, "Could not init state from %s\n", sfile);
		exit(1);
	}
	fclose (seed);

	while(nbytes > 0) {
		mpz_powm_ui (tmpstate, state, 2, n);
		foo = mpz_get_ui (tmpstate);
		fwrite(&foo, sizeof(int), 1, stdout);
		nbytes -= sizeof(int);
		mpz_set(state, tmpstate);
		statesize = mpz_sizeinbase (state, 16);
		fflush(stdout);
	}

	if ((seed = fopen(sfile, "w+")) == NULL) {
		fprintf(stderr, "Could not open %s for saving", sfile); 
		_exit(1);
	}

	if(mpz_out_str(seed, 16, state) == statesize){
		fprintf(stderr, "Wrote state to %s\n", sfile);	
		fflush(seed);
	} else {
		fprintf(stderr, "Could not save state to %s\n", sfile);	
	}
	_exit(1);
}


int gene_primes(FILE *fd) {

	BIGNUM *p, *q, *n;
	BN_CTX *cntx;

	cntx = BN_CTX_new();
	BN_CTX_init(cntx);

	p = BN_new();
	q = BN_new();
	n = BN_new();
	printf("\n\nI will now try to find two prime numbers p,q such that\n\t(p-1)/2 is prime _and_\n\t((p-1)/2-1)/2 is prime.\n\nThis will probably take a _long_ time (several hours on a Intel Pentium III at 466 Mhz)\n\n");

	_gen_p (p, cntx);
	_gen_p (q, cntx);

	if (BN_mul(n, p, q, cntx) == 0) {
		fprintf(stderr, "Error in multiplication??\n");
		exit(1);
	}

	fprintf (fd, "%s\n", BN_bn2dec(n));

	return (1);

}


int usage (void) {
 	fprintf(stdout,"Usage:\n\n\t-g\tgenerate primes and initial seed\n\t-s file\tname of seed file\n\t-m file\tname of modulus file\n\t-n num\tnumber of random bytes to return\n\t-b num\tbitsize of the prime numbers\n");
	exit (0);
}


int _gen_p (BIGNUM *p, BN_CTX *cntx) {

	BIGNUM *q;
	q = BN_new();
	printf ("Generating Germain^2 prime\n");
	while (1) {
		BN_generate_prime(p, bitsize, 1, NULL, NULL, NULL, NULL);
		BN_rshift1(q,p);
		BN_rshift1(q,q);
		if (BN_is_prime_fasttest(q,20,NULL,cntx,NULL,1)) {
			printf( "Found one! The number is:\n%s\n", BN_bn2dec(p));
			break;
		}
		fflush(stdout);
	}
	return;
}
