Click here to Skip to main content
15,893,161 members
Articles / Programming Languages / C++

CBBS: A Pseudo Random Number Generator

Rate me:
Please Sign up or sign in to vote.
4.04/5 (9 votes)
13 Jul 20076 min read 58K   2.5K   23  
A C++ class implementation of the pseudorandom number generator Blum Blum Shub (BBS)
//*********************************************************************************************
//*                                          CBBS                                             *
//*********************************************************************************************
//* Author:		Ravey Alexandre                                                               *
//* Contact:	CBBS@mars105.net                                                              *
//* Date:		07.2007                                                                       *
//* Summary:	A C++ class implementation of the BBS (Blum Blum Shub) algorithm.             *
//*             This is a pseudorandom number generator intended for cryptographic usage.     *
//*                                                                                           *
//* Copyright:	You may use this code as you wish, as long as you do not sell, use it for     *
//*             commercial application, and you let this copyright notice.                    *
//*             Do not take credit for my work, mark your change in the code and all will be  *
//*             fine. If you improve this code in some way, find bugs, or any thing, please   *
//*             let me know.                                                                  *
//*             You may NOT use this code in a commercial application, you do not write it so *
//*             you do not have to be paid for it. If you want make commercial use of this,   *
//*             contact me.                                                                   *
//*                                                                                           *
//* Disclamer:	This software is provided "as is", without warranty of any kind.              *
//*                                                                                           *
//* Version:	1.0                                                                           *
//* History:	1.0:                                                                          *
//*                 Initial release.                                                          *
//*********************************************************************************************

#include "CBBS.h"

CBBS::CBBS()								//Constructor
{
	srand((unsigned int)time(NULL));		//Seed one time, else we get the same "random" data in two request same time

	mpz_init(mpz_M);						//Initialize the mpz
	mpz_init(mpz_X);
	mpz_init(mpz_X0);
	mpz_init(mpz_p);
	mpz_init(mpz_q);
	mpz_init(mpz_p1q1);

	error = 0;
	MaxBit = 0;
	BitIndex = 0;

	Running = false;						//Meens that the generator is not initialised
}

CBBS::~CBBS()								//Destructor
{
	if(Running)								//Clear before exit
		Clear();

	mpz_clear(mpz_M);						//Clear values
	mpz_clear(mpz_X);
	mpz_clear(mpz_X0);
	mpz_clear(mpz_p);
	mpz_clear(mpz_q);
	mpz_clear(mpz_p1q1);
}

int CBBS::Init(char* Seedp, char* Seedq, char* Seedx, unsigned int SeedLen)
{
	if(Running)								//Clear before reinit
		Clear();

	CHECK(SeedLen==0,CBBS_ERROR_SEEDLEN)

	if(GetPrime(mpz_p, SeedLen, Seedp)!=0)	//Generate p
		return error;
	if(GetPrime(mpz_q, SeedLen, Seedq)!=0)	//Generate q
		return error;

	mpz_mul(mpz_M, mpz_p, mpz_q);			//M = pq

	mpz_sub_ui(mpz_p, mpz_p, 1);			//(p-1)
	mpz_sub_ui(mpz_q, mpz_q, 1);			//(q-1)
	mpz_mul(mpz_p1q1, mpz_p, mpz_q);		//(p-1)(q-1)

	SetMaxBit();							//Calculate max bit to take from X and store to MaxBit

	InitX(Seedx, SeedLen);					//Generate X0

	Running = true;							//Set running value to true (generator initialised)

	return 0;
}

int	CBBS::Clear()							//Reset the generator
{
	mpz_set_ui(mpz_M, 0);					//Set all vars to 0
	mpz_set_ui(mpz_X, 0);
	mpz_set_ui(mpz_X0, 0);
	mpz_set_ui(mpz_p, 0);
	mpz_set_ui(mpz_q, 0);
	mpz_set_ui(mpz_p1q1, 0);

	error = 0;
	MaxBit = 0;
	BitIndex = 0;

	return 0;
}

unsigned long CBBS::GetpLen(int b)
{
	CHECK(!Running, CBBS_ERROR_NOINIT)
	CHECK(b<2||b>36, CBBS_ERROR_BASE)

	return (unsigned long)mpz_sizeinbase(mpz_p, b);
}

unsigned long CBBS::GetqLen(int b)
{
	CHECK(!Running, CBBS_ERROR_NOINIT)
	CHECK(b<2||b>36, CBBS_ERROR_BASE)

	return (unsigned long)mpz_sizeinbase(mpz_q, b);
}

unsigned long CBBS::GetMLen(int b)
{
	CHECK(!Running, CBBS_ERROR_NOINIT)
	CHECK(b<2||b>36, CBBS_ERROR_BASE)

	return (unsigned long)mpz_sizeinbase(mpz_M, b);
}

unsigned long CBBS::GetXLen(int b)
{
	CHECK(!Running, CBBS_ERROR_NOINIT)
	CHECK(b<2||b>36, CBBS_ERROR_BASE)

	return (unsigned long)mpz_sizeinbase(mpz_X, b);
}

unsigned long CBBS::GetMaxBit()
{
	CHECK(!Running, CBBS_ERROR_NOINIT)

	return MaxBit;
}

unsigned long CBBS::GetMaxPeriod()
{
	CHECK(!Running, CBBS_ERROR_NOINIT)

	unsigned long i;

	mpz_t mpz_Y;							//Temporary X
	mpz_init(mpz_Y);

	mpz_set(mpz_Y, mpz_X);					//Store X to Y
	i = 0;

	while(mpz_cmp(mpz_Y, mpz_X0)!=0)
	{
		mpz_powm_ui(mpz_Y, mpz_Y, 2, mpz_M);//X = (X�) mod M
		i ++;
	}

	mpz_clear(mpz_Y);

	return i;
}

bool CBBS::GetRndBit()
{
	CHECK(!Running, CBBS_ERROR_NOINIT)

	bool bit;

	if(BitIndex==MaxBit)					//No more bits from this X
		NextX();							//Generate next X
	bit = mpz_tstbit(mpz_X, BitIndex++);	//Get the parity

	return bit;
}

unsigned char CBBS::GetRndByte()
{
	CHECK(!Running, CBBS_ERROR_NOINIT)

	unsigned char	byte;					//Char to return the random value
	int				i;						//Position of the bit to write on the char

	byte = 0;								//Set the byte to 0 (we use OR logical operator to write, so we need it clean)
	i = 7;									//char index, writing from strong bite to lower

	while(i>-1)								//Until byte is filled
	{
		byte |= mpz_tstbit(mpz_X, BitIndex++) << i--;//Get the bite, increment/decrement the counters
		if(BitIndex==MaxBit)				//We take enough bit from X, go to the next one
			NextX();
	}

	return byte;
}

unsigned int CBBS::GetRndInt()
{
	CHECK(!Running, CBBS_ERROR_NOINIT)

	unsigned int	rndint;					//int to return the random value
	int				i;						//Position of the bit to write on the int

	rndint = 0;								//Set the int to 0 (we use OR logical operator to write, so we need it clean)
	i = sizeof(unsigned int)*8;				//int index, writing from strong bite to lower (int size depend of sys)

	while(i>-1)								//Until byte is filled
	{
		rndint |= mpz_tstbit(mpz_X, BitIndex++) << i--;//Get the bite, increment/decrement the counters
		if(BitIndex==MaxBit)				//We take enough bit from X, go to the next one
			NextX();
	}

	return rndint;
}

int CBBS::GetRndBin(unsigned char *rnd, unsigned long Len)
{
	CHECK(!Running, CBBS_ERROR_NOINIT)
	CHECK(rnd==NULL, CBBS_ERROR_PTR)
	CHECK(Len==0, CBBS_ERROR_LEN)

	unsigned char	*rndptr;				//Position of the data to write
	unsigned char	byte;					//temp byte
	int				i;						//Position of the bit to write on the int

	memset(rnd, 0, Len);					//Set rnd to 0 data
	rndptr = rnd;							//Firt char

	while(rndptr<(rnd+Len))
	{
		i = 8;								//byte index, writing from strong bite to lower
		byte = 0;							//Set the byte to 0 (we use OR logical operator to write, so we need it clean)

		while(i>-1)							//Until byte is filled
		{
			byte |= mpz_tstbit(mpz_X, BitIndex++) << i--;//Get the bite, increment/decrement the counters
			if(BitIndex==MaxBit)			//We take enough bit from X, go to the next one
				NextX();
		}
		memcpy(rndptr++, &byte, 1);			//Copy the byte to data
	}

	return 0;
}

int CBBS::GetRndBinHex(char *rnd, unsigned long Len)
{
	CHECK(!Running, CBBS_ERROR_NOINIT)
	CHECK(rnd==NULL, CBBS_ERROR_PTR)
	CHECK(Len==0, CBBS_ERROR_LEN)

	unsigned int	rndLen;
	unsigned char	*rndtmp, *rndtmppos;
	char			*rndpos;

	rndLen = (Len-1)/2;						//Set the random binary length. typcast to int automaticly floor the value.
	rndtmp = new unsigned char[rndLen];		//Temporary space for the random data

	GetRndBin(rndtmp, rndLen);				//Get somme random data from generator

	rndpos = rnd;							//rndpos, position in the write buffer
	rndtmppos = rndtmp;						//rndtmppos, position in the random data

	while(rndtmppos<(rndtmp+rndLen))		//Copy data to pstr in hex, eye-readable format
		rndpos += sprintf(rndpos, "%02x", *rndtmppos++);

	delete [] rndtmp;

	return 0;
}

bool CBBS::GetRndBitAt(unsigned long i, int offset)
{
	CHECK(!Running, CBBS_ERROR_NOINIT)
	CHECK(i==0, CBBS_ERROR_I)
	CHECK(offset>MaxBit, CBBS_ERROR_OFFSET)

	bool	bit;							//Contain the return bit      i
	mpz_t	mpz_exp;						//to calculate the exp of X (2  mod (p-1)(q-1))
	mpz_t	mpz_tmp;						//Temp value (to store the 2 and after, the Xi)

	mpz_init(mpz_exp);
	mpz_init(mpz_tmp);

	mpz_set_ui(mpz_tmp, 2);

	//Formula for calculating X at i
	//        i
	//       2  mod (p-1)(q-1)
	//X  = (X                  ) mod M
	// i     0

	mpz_powm_ui(mpz_exp, mpz_tmp, i, mpz_p1q1);	//2i mod (p-1)(q-1)
	mpz_powm(mpz_tmp, mpz_X0, mpz_exp, mpz_M);	//X0exp mod M
#ifdef CBBS_PRINT_X_INFOS
	printf("\nTmp X: %s\n", mpz_get_str(NULL, 16, mpz_tmp));
#endif //CBBS_PRINT_X_INFOS
	bit = mpz_tstbit(mpz_tmp, offset);		//Get the parity

	mpz_clear(mpz_exp);
	mpz_clear(mpz_tmp);

	return bit;
}

int CBBS::GetSysRnd(char* Dest, unsigned int Len)
{
	unsigned int i;

	for(i=0;i<Len;i++)
		Dest[i] = (char)rand();				//So bad... so, you wanted it... take some *bad* random data from OS.

	return 0;
}

int	CBBS::InitX(char *Seed, unsigned int SeedLen)
{
	char	*rnd,  *rndpos;					//Random data
	mpz_t	mpz_g;

	CHECK(SeedLen==0, CBBS_ERROR_SEEDLEN)

	//No Seed provided, generating one.
	if(Seed == NULL)
	{
		rndpos  = rnd  = new char[SeedLen];

		GetSysRnd(rnd, SeedLen);			//Get some random data from OS
		CharToMpz(mpz_X, rnd, SeedLen);		//Convert random number to mpz
		delete [] rnd;						//Free the buffers
	}
	else //A Seed has been provided, using it
	{
		CharToMpz(mpz_X, Seed, SeedLen);	//Convert seed to mpz
	}

	mpz_init(mpz_g);

	do										//gcd(X,M) must be 1
	{
		mpz_add_ui(mpz_X, mpz_X, 1);		//Add 1 to X
		mpz_gcd(mpz_g, mpz_X, mpz_M);		//Find the Greatest common divisor from X and M
	}
	while(mpz_cmp_ui(mpz_g, 1)!=0);			//If the gcd is not 1, get next X and recheck

	mpz_clear(mpz_g);

	NextX();								//Make X0 (the previous was X-1)
	mpz_set(mpz_X0, mpz_X);					//Store X0 for further check
	NextX();								//Make X1, we don't use X0
	BitIndex = 0;							//Index of the bit to take from X, 0 = first light weight bit

#ifdef CBBS_PRINT_X_INFOS
	printf("\nX0: %s\n", mpz_get_str(NULL, 16, mpz_X0));
	printf("\nX1: %s\n", mpz_get_str(NULL, 16, mpz_X));
#endif //CBBS_PRINT_X_INFOS
	return 0;
}

int CBBS::NextX()
{
	mpz_powm_ui(mpz_X, mpz_X, 2, mpz_M);	//X = (X�) mod M
#ifdef CBBS_PRINT_X_INFOS
	printf("\nNew X: %s\n", mpz_get_str(NULL, 16, mpz_X));
#endif //CBBS_PRINT_X_INFOS

	BitIndex = 0;							//Continue to take bit from first

#ifdef CBBS_PERIOD_CHECK
	if(mpz_cmp(mpz_X0, mpz_X)==0)			//Check if X = X0
		error = CBBS_ERROR_LOOP;			//The generator is in loop, max period here
#endif //CBBS_PERIOD_CHECK					//But we continue, just notice the error

	return 0;
}

int CBBS::SetMaxBit()
{
	mpfr_t			mpfr_l, mpfr_M;			//temp mpfr var
	mpfr_rnd_t		mpfr_rnd;

	mpfr_init(mpfr_l);						//Init temp var
	mpfr_init(mpfr_M);

	mpfr_rnd = mpfr_get_default_rounding_mode();//Get round mode to rnd (just for code lisibility)

	mpfr_set_z(mpfr_M, mpz_M, mpfr_rnd);	//put M in mpfrM for use with MPFR
	mpfr_log2(mpfr_l, mpfr_M, mpfr_rnd);	//Calc log2(M)
	mpfr_log2(mpfr_l, mpfr_l, mpfr_rnd);	//Calc log2(log2(M))
	mpfr_floor(mpfr_l, mpfr_l);				//Round to lower int (if we can take 2.9 bit we take only 2. obvious)
	MaxBit = mpfr_get_ui(mpfr_l, mpfr_rnd);	//Store the number in MaxBit

	mpfr_clear(mpfr_M);						//temp can ben deleted
	mpfr_clear(mpfr_l);

	return 0;
}

int CBBS::GetPrime(mpz_t mpz_p, unsigned int SeedLen, char* Seed)
{
	char	*rnd,  *rndpos;					//Random data

	CHECK(SeedLen==0, CBBS_ERROR_SEEDLEN)

	//No Seed provided, generating one.
	if(Seed == NULL)
	{
		rndpos  = rnd  = new char[SeedLen];

		GetSysRnd(rnd, SeedLen);			//Get some random data from OS
		rnd[SeedLen] |= 0x01;				//Set the first bit of p, ensure that p is odd
		rnd[0] |= 0x80;						//Set the last bit of p, ensure that the number is max length
		CharToMpz(mpz_p, rnd, SeedLen);		//Convert random number to mpz
		delete [] rnd;						//Free the buffers
	}
	else //A Seed has been provided, using it
	{
		Seed[SeedLen] |= 0x01;				//Set the first bit of p, ensure that p is odd
		Seed[0] |= 0x80;					//Set the last bit of p, ensure that the number is max length
		CharToMpz(mpz_p, Seed, SeedLen);	//Convert seed to mpz
	}

	//While p is not a prime number and p is not congruent to 3 (mod 4)
	while((!mpz_probab_prime_p(mpz_p, CBBS_PROB_PRIME))||(mpz_fdiv_ui(mpz_p, 4)!=3))
		mpz_nextprime(mpz_p, mpz_p);		//Get the next prime number to p

	//At this point we must have a p wich is prime and congruent to 3 (mod 4)
	return 0;
}

int CBBS::CharToMpz(mpz_t mpz_mpz, char *chr, unsigned int Len)
{
	char	*pstr,  *pstrpos;				//Temp buffer
	char	*chrpos;

	pstrpos = pstr = new char[(Len*2)+1];	//Size of data *2 (for hex) and +1 for zero trailing
	chrpos = chr;							//Getting start pos

	while(chrpos<(chr+Len))					//Copy data to pstr in hex, eye-readable format
		pstrpos += sprintf(pstrpos, "%02x", (unsigned char)*chrpos++);

	mpz_init_set_str(mpz_mpz, pstr, 16);	//Put the number in mpz using pstr base 16

	delete [] pstr;							//Free the buffer

	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions