aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/rsa/PrimeGenerator.cpp
blob: f476a30d20086a847ff715af8ba1f60d86eba559 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/* ****************************************************************************
 *
 * Copyright 2013 Nedim Srndic
 * 
 * This file is part of rsa - the RSA implementation in C++.
 *
 * 				PrimeGenerator.cpp
 * 
 * Author: Nedim Srndic
 * Release date: 14th of March 2008
 * 
 * This file contains the implementation for the PrimeGenerator class.
 * 
 * There is a static constant defined in this file: 
 * 
 * - RandMax		: RAND_MAX (defined in cstdlib) of type BigInt
 * 		Mainly used for speedup in the Generate member function. 
 * 		Represents the largest random unsigned long integer that a particular 
 * 		platform can generate. This is platform-specific. 
 * 	 
 * ****************************************************************************
 */

#include "PrimeGenerator.h"
#include <string>
#include <cstdlib> // rand()

/* Generates a random number with digitCount digits.
 * Returns it by reference in the "number" parameter. */
void PrimeGenerator::MakeRandom(BigInt &number, unsigned long int digitCount)
{
	//the new number will be created using a string object (newNum), 
	//and later converted into a BigInt
	std::string newNum;
	newNum.resize(digitCount);
	unsigned long int tempDigitCount(0);

	//generate random digits
	while (tempDigitCount < digitCount)
	{
		unsigned long int newRand(std::rand());

		//10 is chosen to skip the first digit, because it might be 
		//statistically <= n, where n is the first digit of RAND_MAX
		while (newRand >= 10)
		{
			newNum[tempDigitCount++] = (newRand % 10) + '0';
			newRand /= 10;
			if (tempDigitCount == digitCount)
				break;
		}
	}

	//make sure the leading digit is not zero
	if (newNum[0] == '0')
		newNum[0] = (std::rand() % 9) + 1 + '0';
	number = newNum;
}

/* Generates a random number such as 1 <= number < 'top'.
 * Returns it by reference in the 'number' parameter. */
void PrimeGenerator::makeRandom(BigInt &number, const BigInt &top)
{
	//randomly select the number of digits for the random number
	unsigned long int newDigitCount = (rand() % top.Length()) + 1;
	MakeRandom(number, newDigitCount);
	//make sure number < top
	while (number >= top)
		MakeRandom(number, newDigitCount);
}

/* Creates an odd BigInt with the specified number of digits. 
 * Returns it by reference in the "number" parameter. */
void PrimeGenerator::makePrimeCandidate(BigInt &number,
										unsigned long int digitCount)
{
	PrimeGenerator::MakeRandom(number, digitCount);
	//make the number odd
	if (!number.IsOdd())
		number.SetDigit(0, number.GetDigit(0) + 1);
	//make sure the leading digit is not a zero
	if (number.GetDigit(number.Length() - 1) == 0)
		number.SetDigit(number.Length() - 1, (std::rand() % 9) + 1);
}

/* Tests the primality of the given _odd_ number using the 
 * Miller-Rabin probabilistic primality test. Returns true if 
 * the tested argument "number" is a probable prime with a 
 * probability of at least 1 - 4^(-k), otherwise false. */
bool PrimeGenerator::isProbablePrime(	const BigInt &number, 
										unsigned long int k)
{
	//first we need to calculate such a and b, that
	//number - 1 = 2^a * b, a and b are integers, b is odd
	BigInt numberMinusOne(number - BigIntOne);
	unsigned long int a(0);
	BigInt temp(numberMinusOne);
	BigInt b, quotient;
	static const BigInt two(BigIntOne + BigIntOne);

	while (b.EqualsZero())
	{
		//temp = quotient * 2 + remainder
		
		//PrimeGenerator used to be a friend of BigInt, so the following 
		//statement produced the result in one call to BigInt::divide()
//		BigInt::divide(temp, two, quotient, b);
		//That doesn't work any more, so we have to use two calls
		quotient = temp / two;
		b = temp % two;
		temp = quotient;
		a++;
	}
	b = temp * two + b;
	a--;

	//test with k different possible witnesses to ensure that the probability
	//that "number" is prime is at least 1 - 4^(-k)
	for (unsigned long int i = 0; i < k; i++)
	{
		PrimeGenerator::makeRandom(temp, number);
		
		if (isWitness(temp, number, b, a, numberMinusOne))
			return false; //definitely a composite number
	}
	return true; //a probable prime
}

/* Returns true if "candidate" is a witness for the compositeness
 * of "number", false if "candidate" is a strong liar. "exponent" 
 * and "squareCount" are used for computation */
bool PrimeGenerator::isWitness(	BigInt candidate, 
								const BigInt &number, 
								const BigInt &exponent, 
								unsigned long int squareCount, 
								const BigInt &numberMinusOne)
{
	//calculate candidate = (candidate to the power of exponent) mod number
	candidate.SetPowerMod(exponent, number);
	//temporary variable, used to call the divide function
	BigInt quotient;

	for (unsigned long int i = 0; i < squareCount; i++)
	{
		bool maybeWitness(false);
		if (candidate != BigIntOne && candidate != numberMinusOne)
			maybeWitness = true;

		//PrimeGenerator used to be a friend of BigInt, so the following 
		//statement produced the result in one call to BigInt::divide()
//		BigInt::divide(candidate * candidate, number, quotient, candidate);
		//That doesn't work any more, so we have to use two calls
		candidate = candidate * candidate;
		quotient = (candidate) / number;
		candidate = (candidate) % number;
		if (maybeWitness && candidate == BigIntOne)
			return true; //definitely a composite number
	}

	if (candidate != BigIntOne)
		return true; //definitely a composite number

	return false; //probable prime
}

/* Returns a probable prime number "digitCount" digits long, 
 * with a probability of at least 1 - 4^(-k) that it is prime. */
BigInt PrimeGenerator::Generate(unsigned long int digitCount, 
								unsigned long int k)
{
	if (digitCount < 3)
		throw "Error PRIMEGENERATOR00: Primes less than 3 digits long "
				"not supported.";
	
	BigInt primeCandidate;
	PrimeGenerator::makePrimeCandidate(primeCandidate, digitCount);
	while (!isProbablePrime(primeCandidate, k))
	{
		//select the next odd number and try again
		primeCandidate = primeCandidate + 2;
		if (primeCandidate.Length() != digitCount)
		PrimeGenerator::makePrimeCandidate(primeCandidate, digitCount);
	}
	return primeCandidate;
}