/****************************************************************************/
/* Algoritmo de Blum-Blum-Shub para generación de números pseudoaleatorios. */
/*                                                                          */
/* Jaime Suarez <mcripto@bigfoot.com> 2003                                  */
/* en http://elparaiso.mat.uned.es                                            */
/****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gmp.h"

/* Este es el tamaño aproximado del módulo n */
#define BITS_MODULO 1024

/* Inicializa el generador de números aleatorios, s es una cadena con un
 * número en base 10 que sirve de semilla. Es importante que sea lo más
 * aleatorio posible y de un tamaño similar al de n */
void iniciarBBS(char *s);

/* Devuelve un bit aleatorio de un generador ya inicializado */
int bitBBS(void);

/* Devuelve un byte aleatorio de un generador BBS ya inicializado */
int byteBBS(void);


/*
 * Comienzo del programa
 */

int main(int argc, char *argv[])
{
        int i, nBytes;
        unsigned char s[256];
        FILE *fo;
        
        if (argc!=3) {
         printf("------------------------------------------------------\n");
         printf(" Generador de números pseudoaleatorios Blum-Blum-Shub.\n\n");
         printf(" Uso: %s <n> <s>\n",argv[0]);
         printf(" n: numero de bytes deseados.\n");
         printf(" s: semilla, cadena de caracteres arbitrarios.\n");
         printf("------------------------------------------------------\n");
         return 1;
        }

        /* Leer línea de comandos */
        nBytes=atoi(argv[1]);
        strncpy(s,argv[2],256);
        /* Convertir la cadena s en un entero */
        for (i=0; i<strlen(s); i++) s[i]=(s[i]%10)+'0';
        
        /* Inicializa generador y produce los bytes pedidos 
         * guardándolos en bbs.out */
        iniciarBBS(s);
        fo = fopen("bbs.out", "wb"); 
        for (i=0; i<nBytes; i++) {
                fprintf(fo, "%c", byteBBS());
        }
        fclose(fo);
        puts("Resultados en el fichero: bbs.out");
        return 0;
}

mpz_t x;                        /* Último valor aleatorio */
mpz_t n;                        /* Módulo para BBS */

/*
 *  Inicia el generador de numeros aleatorios a partir de la cadena s
 *  que contiene un entero en base 10 que sirve como semilla.
 */
void iniciarBBS(char *s)
{
        mpz_t p, q, tmp;
        gmp_randstate_t estado;

        /* Inicializar rng de la librería gmp3 */
        gmp_randinit_default(estado);
        mpz_set_str(tmp, s, 10);
        gmp_randseed(estado, tmp);

        /* Inicializar enteros largos */
        mpz_init(x); mpz_init(n); mpz_init(p); mpz_init(q);

        /* Tenemos que generar n como producto
         * de dos grandes primos congruentes con 3 modulo 4 */
        do {
                mpz_urandomb(p, estado, BITS_MODULO/2);
                mpz_mul_ui(p,p,4);
                mpz_add_ui(p,p,3);
        
        } while (mpz_probab_prime_p(p,25)==0);

        do {
                mpz_urandomb(q, estado, BITS_MODULO/2);
                mpz_mul_ui(q,q,4);
                mpz_add_ui(q,q,3);
        
        } while (mpz_probab_prime_p(q,25)==0);
        mpz_mul(n,p,q);

        /* Ahora se produce la primera x = s^2 (mod n)  */
        mpz_set_str(x,s,10);
        mpz_mod(x,x,n);
        mpz_mul(x,x,x);
        mpz_mod(x,x,n);

        /* Limpiamos variables innecesarias en lo sucesivo */
        mpz_clear(p); mpz_clear(q);
        return;
}

/*                                                                    */
/*  Genera un bit pseudoaleatorio a partir de la variable global x    */
/*  previamente existente. Es necesario llamar a iniciarBBS antes de  */
/*  utilizarlo.                                                       */

int bitBBS(void)
{
        mpz_mul(x,x,x);        
        mpz_mod(x,x,n);                 /* x = x^2 mod n                        */
        
        return mpz_tstbit(x, 0); /* devolver el bit menos significativo        */ 
}

/*
 * Devuelve un byte pseudoaleatorio. Debe llamarse una vez a iniciarBBS
 * antes de comenzar a pedir bytes.
 */
int byteBBS(void)
{
        int byte=0, i;

        for (i=0; i<8; i++) 
                byte = byte*2 + bitBBS();
        
        return byte;
}