Removed useless rsalib library - too slow!
parent
ab159a3887
commit
0b8d4563ac
File diff suppressed because it is too large
Load Diff
|
@ -1,277 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* BigInt.h
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 14th of March 2008
|
||||
*
|
||||
* A class representing a positive or negative integer that may
|
||||
* be too large to fit in any of the standard C++ integer types
|
||||
* (i. e. 2^128 is "just" 39 digits long).
|
||||
* The digits are stored in a dinamic array of tipe unsigned char*,
|
||||
* with values from 0 to 9 (not '0' to '9'), so that the CPU can
|
||||
* add/subtract individual digits.
|
||||
*
|
||||
* The array has "length" memory locations, one byte each (the size of
|
||||
* unsigned char is probably one byte). There are "digitCount" digits actually
|
||||
* in use, the rest is spare space.
|
||||
* The number of digits is constrained by available memory and the limit of the
|
||||
* unsigned long int type used for indexing (the "length" property).
|
||||
* The individual digits are stored right-to-left, to speed up computing and
|
||||
* allow for faster growth of numbers (no need to reallocate memory when
|
||||
* the digitCount grows).
|
||||
*
|
||||
* The class handles its own memory management. There are no memory leaks
|
||||
* reported until this date.
|
||||
* When creating a BigInt from const char* or unsigned long int,
|
||||
* copying from an other BigInt with (digitCount + 2 <= length)
|
||||
* (soon to be full), new memory is allocated and
|
||||
* length is adjusted to (length * FACTOR + 1). This is done to expand the
|
||||
* capacity of the digits array to accomodate potential new digits.
|
||||
* When assigning a BigInt "bInt" that is twice as small or bigger than *this,
|
||||
* the length is set to (bInt.length + 2).
|
||||
*
|
||||
* BigInt supports:
|
||||
*
|
||||
* - addition (unary +, binary +, +=, prefix ++, postfix ++)
|
||||
*
|
||||
* - subtraction (unary -, binary -, -=, prefix --, postfix --)
|
||||
*
|
||||
* - multiplication (*, *=)
|
||||
* For multiplication, one can choose between the Square and multiply
|
||||
* or Karatsuba algorithm, or long multiplication at compile time
|
||||
* (this can be done by defining or undefining the macro "KARATSUBA"
|
||||
* in BigInt.cpp).
|
||||
* The Karatsuba algorithm multiplies integers in O(n^log2(3))
|
||||
* complexity. log2(3) is approximately 1.585, so this should be
|
||||
* significantly faster than long multiplication, if the numbers are
|
||||
* big enough. Currently, the long multiplication is better implemented,
|
||||
* and runs faster than the Karatsuba multiplication for numbers shorter
|
||||
* than about 100 digits.
|
||||
*
|
||||
* - C-style integer division (/, /=)
|
||||
*
|
||||
* - C-style integer division remainder (%, %=)
|
||||
* When calculating the remainder, the number is first divided.
|
||||
*
|
||||
* - comparison (==, !=, <, <=, >, >=)
|
||||
* All of the <, <=, >, >= operators are equally fast.
|
||||
*
|
||||
* - exponentiation (GetPower(), SetPower(), GetPowerMod(), SetPowerMod())
|
||||
* For exponentiation, the Exponantiation by squaring
|
||||
* (or Square and multiply or Binary exponentiation) algorithm is used.
|
||||
* It uses O(log(n)) multiplications and therefore is significantly faster
|
||||
* than multiplying x with itself n-1 times.
|
||||
*
|
||||
* In addition to mathematical operations, BigInt supports:
|
||||
*
|
||||
* - automatic conversion from const char *, std::string and unsigned long int
|
||||
* - safe construction, copying, assignment and destruction
|
||||
* - automatic conversion to std::string
|
||||
* - writing to the standard output (operator <<(std::ostream, BigInt))
|
||||
* - reading from the standard input (operator >>(std::istream, BigInt))
|
||||
* - getting and setting individual digits (GetDigit(), SetDigit())
|
||||
* - returning the number of digits (Length())
|
||||
* - returning a string of digits (ToString())
|
||||
* This can be useful for human-readable output.
|
||||
* - returning a value indicating wether the number is odd (IsOdd())
|
||||
* - returning a value indicating wether the number is positive (IsPositive())
|
||||
* - returning a value indicating wether the BigInt equals zero (EqualsZero())
|
||||
* The fastest way to determine this.
|
||||
* - returning absolute value (Abs())
|
||||
*
|
||||
* There are a few static constants defined in this file:
|
||||
*
|
||||
* - BigIntZero : a zero of type BigInt
|
||||
* If you need a zero fast, use this.
|
||||
* - BigIntOne : a one of type BigInt
|
||||
* If you need a one fast, use this.
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef BIGINT_H_
|
||||
#define BIGINT_H_
|
||||
|
||||
#include <iostream> //ostream, istream
|
||||
#include <cmath> //sqrt()
|
||||
#include <string> //ToString(), BigInt(std::string)
|
||||
|
||||
class BigInt
|
||||
{
|
||||
private:
|
||||
/* An array of digits stored right to left,
|
||||
* i.e. int 345 = unsigned char {[5], [4], [3]} */
|
||||
unsigned char *digits;
|
||||
// The total length of the allocated memory
|
||||
unsigned long int length;
|
||||
// Number of digits
|
||||
unsigned long int digitCount;
|
||||
// Sign
|
||||
bool positive;
|
||||
/* Multiplication factor for the length property
|
||||
* when creating or copying objects. */
|
||||
static const double FACTOR;
|
||||
/* Transforms the number from unsigned long int to unsigned char[]
|
||||
* and pads the result with zeroes. Returns the number of digits. */
|
||||
static unsigned long int int2uchar( unsigned long int number,
|
||||
unsigned char *digits,
|
||||
unsigned long int padding);
|
||||
/* Converts ASCII digits to equivalent unsigned char numeric values. */
|
||||
static void char2uchar( unsigned char *array,
|
||||
unsigned long int length);
|
||||
/* Check if all ASCII values are digits '0' to '9'. */
|
||||
static bool allCharsAreDigits( const char *array,
|
||||
unsigned long int length);
|
||||
/* Compares two BigInt. If the last two arguments are
|
||||
* omitted, the comparison is sign-insensitive (comparison by
|
||||
* absolute value). Returns 0 if a == b, 1 if a > b, 2 if a < b. */
|
||||
static int compareNumbers( unsigned char *a, unsigned long int na,
|
||||
unsigned char *b, unsigned long int nb,
|
||||
bool aPositive = true,
|
||||
bool bPositive = true);
|
||||
/* Multiplies two unsigned char[] using the Divide and Conquer
|
||||
* a.k.a. Karatsuba algorithm .*/
|
||||
static void karatsubaMultiply( unsigned char *a, unsigned char *b,
|
||||
unsigned long int n,
|
||||
unsigned char *buffer);
|
||||
/* Multiplies two unsigned char[] the long way. */
|
||||
static void longMultiply( unsigned char *a, unsigned long int na,
|
||||
unsigned char *b, unsigned long int nb,
|
||||
unsigned char *result);
|
||||
/* Simple addition, used by the multiply function.
|
||||
* Returns the remaining carry. */
|
||||
static unsigned char quickAdd( unsigned char *a, unsigned char *b,
|
||||
unsigned long int n);
|
||||
/* Simple subtraction, used by the multiply function. */
|
||||
static void quickSub( unsigned char *a, unsigned char *b,
|
||||
unsigned char *end, unsigned long int n);
|
||||
/* Divides two BigInt numbers. */
|
||||
static void divide( const BigInt ÷nd, const BigInt &divisor,
|
||||
BigInt "ient, BigInt &remainder);
|
||||
/* Returns the value of the specified unsigned char[] as long int. */
|
||||
static unsigned long int toInt(unsigned char *digits, int n);
|
||||
/* Saves the sum of two unsigned char* shorter and longer into result.
|
||||
* It must be nShorter <= nLonger. If doFill == true, it fills the
|
||||
* remaining free places with zeroes (used in KaratsubaMultiply()).
|
||||
* Returns true if there was an overflow at the end (meaning that
|
||||
* the result.digitCount was longer.digitCount + 1. */
|
||||
static bool add(unsigned char *shorter, unsigned long int nShorter,
|
||||
unsigned char *longer, unsigned long int nLonger,
|
||||
unsigned char *result, int nResult,
|
||||
bool doFill = true);
|
||||
/* Shifts the digits n places left. */
|
||||
BigInt &shiftLeft(unsigned long int n);
|
||||
/* Shifts the digits n places right. */
|
||||
BigInt &shiftRight(unsigned long int n);
|
||||
/* Expands the digits* to n. */
|
||||
void expandTo(unsigned long int n);
|
||||
public:
|
||||
BigInt();
|
||||
BigInt(const char *charNum);
|
||||
BigInt(unsigned long int intNum);
|
||||
BigInt(const std::string &str);
|
||||
BigInt(const BigInt &number);
|
||||
BigInt &operator =(const BigInt &rightNumber);
|
||||
~BigInt();
|
||||
operator std::string() const;
|
||||
friend std::ostream &operator <<( std::ostream &cout,
|
||||
const BigInt &number);
|
||||
friend std::istream &operator >>( std::istream &cin,
|
||||
BigInt &number);
|
||||
friend bool operator <(const BigInt &a, const BigInt &b);
|
||||
friend bool operator <=(const BigInt &a, const BigInt &b);
|
||||
friend bool operator >(const BigInt &a, const BigInt &b);
|
||||
friend bool operator >=(const BigInt &a, const BigInt &b);
|
||||
friend bool operator ==(const BigInt &a, const BigInt &b);
|
||||
friend bool operator !=(const BigInt &a, const BigInt &b);
|
||||
friend BigInt operator + (const BigInt &a, const BigInt &b);
|
||||
BigInt &operator+();
|
||||
BigInt &operator++();
|
||||
BigInt operator++(int);
|
||||
BigInt &operator+=(const BigInt &number);
|
||||
BigInt operator-() const;
|
||||
friend BigInt operator-(const BigInt &a, const BigInt &b);
|
||||
BigInt &operator--();
|
||||
BigInt operator--(int);
|
||||
BigInt &operator-=(const BigInt &number);
|
||||
friend BigInt operator*(const BigInt &a, const BigInt &b);
|
||||
BigInt &operator*=(const BigInt &number);
|
||||
friend BigInt operator/(const BigInt &a, const BigInt &b);
|
||||
BigInt &operator/=(const BigInt &number);
|
||||
friend BigInt operator%(const BigInt &a, const BigInt &b);
|
||||
BigInt &operator%=(const BigInt &number);
|
||||
/* Returns *this to the power of n
|
||||
* using the fast Square and Multiply algorithm. */
|
||||
BigInt GetPower(unsigned long int n) const;
|
||||
/* *this = *this to the power of n. */
|
||||
void SetPower(unsigned long int n);
|
||||
/* Returns *this to the power of n
|
||||
* using the fast Square and Multiply algorithm. */
|
||||
BigInt GetPower(BigInt n) const;
|
||||
/* *this = *this to the power of n. */
|
||||
void SetPower(BigInt n);
|
||||
/* Returns (*this to the power of b) mod n. */
|
||||
BigInt GetPowerMod(const BigInt &b, const BigInt &n) const;
|
||||
/* *this = (*this to the power of b) mod n. */
|
||||
void SetPowerMod(const BigInt &b, const BigInt &n);
|
||||
/* Returns the 'index'th digit (zero-based, right-to-left). */
|
||||
unsigned char GetDigit(unsigned long int index) const;
|
||||
/* Sets the value of 'index'th digit
|
||||
* (zero-based, right-to-left) to 'value'. */
|
||||
void SetDigit(unsigned long int index, unsigned char value);
|
||||
/* Returns the number of digits. */
|
||||
unsigned long int Length() const;
|
||||
/* Returns true if *this is positive, otherwise false. */
|
||||
bool IsPositive() const;
|
||||
/* Returns true if *this is odd, otherwise false. */
|
||||
bool IsOdd() const;
|
||||
/* Returns the value of BigInt as std::string. */
|
||||
std::string ToString(bool forceSign = false) const;
|
||||
/* Returns a value indicating whether *this equals 0. */
|
||||
bool EqualsZero() const;
|
||||
/* Returns the absolute value. */
|
||||
BigInt Abs() const;
|
||||
};
|
||||
|
||||
inline BigInt::~BigInt()
|
||||
{
|
||||
delete[] digits;
|
||||
}
|
||||
|
||||
inline BigInt &BigInt::operator+()
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Returns the number of digits. */
|
||||
inline unsigned long int BigInt::Length() const
|
||||
{
|
||||
return digitCount;
|
||||
}
|
||||
|
||||
/* Returns true if *this is positive, otherwise false. */
|
||||
inline bool BigInt::IsPositive() const
|
||||
{
|
||||
return positive;
|
||||
}
|
||||
|
||||
/* Returns true if *this is odd, otherwise false. */
|
||||
inline bool BigInt::IsOdd() const
|
||||
{
|
||||
return digits[0] & 1;
|
||||
}
|
||||
|
||||
/* Returns a value indicating whether *this equals 0. */
|
||||
inline bool BigInt::EqualsZero() const
|
||||
{
|
||||
return digitCount == 1 && digits[0] == 0;
|
||||
}
|
||||
|
||||
// A BigInt number with the value of 0.
|
||||
static const BigInt BigIntZero;
|
||||
// A BigInt number with the value of 1.
|
||||
static const BigInt BigIntOne(1L);
|
||||
|
||||
|
||||
#endif /*BIGINT_H_*/
|
|
@ -1,20 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* Key.cpp
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 5th of September 2008
|
||||
*
|
||||
* This file contains the implementation for the Key class.
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#include "Key.h"
|
||||
|
||||
std::ostream &operator<<(std::ostream &cout, const Key &key)
|
||||
{
|
||||
return std::cout
|
||||
<< "Modulus: " << key.GetModulus() << std::endl
|
||||
<< "Exponent: " << key.GetExponent();
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* Key.h
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 16th of June 2008
|
||||
*
|
||||
* A class representing a public or private RSA key.
|
||||
*
|
||||
* A public or private RSA key consists of a modulus and an exponent. In this
|
||||
* implementation an object of type BigInt is used to store those values.
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef KEY_H_
|
||||
#define KEY_H_
|
||||
|
||||
#include "BigInt.h"
|
||||
#include <iostream>
|
||||
|
||||
class Key
|
||||
{
|
||||
private:
|
||||
BigInt modulus;
|
||||
BigInt exponent;
|
||||
public:
|
||||
Key(const BigInt &modulus, const BigInt &exponent) :
|
||||
modulus(modulus), exponent(exponent)
|
||||
{}
|
||||
const BigInt &GetModulus() const
|
||||
{
|
||||
return modulus;
|
||||
}
|
||||
const BigInt &GetExponent() const
|
||||
{
|
||||
return exponent;
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &cout, const Key &key);
|
||||
};
|
||||
|
||||
#endif /*KEY_H_*/
|
|
@ -1,20 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* KeyPair.cpp
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 22th of July 2008
|
||||
*
|
||||
* This file contains the implementation for the KeyPair class.
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#include "KeyPair.h"
|
||||
|
||||
std::ostream &operator <<(std::ostream &cout, const KeyPair &k)
|
||||
{
|
||||
return std::cout
|
||||
<< "Private key:" << std::endl << k.GetPrivateKey() << std::endl
|
||||
<< "Public key:" << std::endl << k.GetPublicKey();
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* KeyPair.h
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 17th of June 2008
|
||||
*
|
||||
* A class representing a public/private RSA keypair.
|
||||
*
|
||||
* A keypair consists of a public key and a matching private key.
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef KEYPAIR_H_
|
||||
#define KEYPAIR_H_
|
||||
|
||||
#include "Key.h"
|
||||
#include <iostream>
|
||||
|
||||
class KeyPair
|
||||
{
|
||||
private:
|
||||
const Key privateKey;
|
||||
const Key publicKey;
|
||||
public:
|
||||
KeyPair(Key privateKey, Key publicKey):
|
||||
privateKey(privateKey), publicKey(publicKey)
|
||||
{}
|
||||
const Key &GetPrivateKey() const
|
||||
{
|
||||
return privateKey;
|
||||
}
|
||||
const Key &GetPublicKey() const
|
||||
{
|
||||
return publicKey;
|
||||
}
|
||||
friend std::ostream &operator <<(std::ostream &cout, const KeyPair &k);
|
||||
};
|
||||
|
||||
#endif /*KEYPAIR_H_*/
|
|
@ -1,181 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* 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;
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
* PrimeGenerator.h
|
||||
*
|
||||
* A class used to generate large prime or random numbers.
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 14th of March 2008
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef PRIMEGENERATOR_H_
|
||||
#define PRIMEGENERATOR_H_
|
||||
|
||||
#include "BigInt.h"
|
||||
|
||||
class PrimeGenerator
|
||||
{
|
||||
private:
|
||||
/* Generates a random "number" such as 1 <= "number" < "top".
|
||||
* Returns it by reference in the "number" parameter. */
|
||||
static void makeRandom( BigInt &number,
|
||||
const BigInt &top);
|
||||
/* Creates an odd BigInt with the specified number of digits.
|
||||
* Returns it by reference in the "number" parameter. */
|
||||
static void makePrimeCandidate( BigInt &number,
|
||||
unsigned long int digitCount);
|
||||
/* 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. */
|
||||
static bool isProbablePrime(const BigInt &number,
|
||||
unsigned long int k);
|
||||
/* 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 */
|
||||
static bool isWitness( BigInt candidate,
|
||||
const BigInt &number,
|
||||
const BigInt &exponent,
|
||||
unsigned long int squareCount,
|
||||
const BigInt &numberMinusOne);
|
||||
public:
|
||||
/* Generates a random number with digitCount digits.
|
||||
* Returns it by reference in the "number" parameter. */
|
||||
static void MakeRandom( BigInt &number,
|
||||
unsigned long int digitCount);
|
||||
/* Returns a probable prime number "digitCount" digits long,
|
||||
* with a probability of at least 1 - 4^(-k) that it is prime. */
|
||||
static BigInt Generate( unsigned long int digitCount,
|
||||
unsigned long int k = 3);
|
||||
};
|
||||
|
||||
#endif /*PRIMEGENERATOR_H_*/
|
|
@ -1,377 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* RSA.cpp
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 16th of June 2008
|
||||
*
|
||||
* This file contains the implementation for the RSA class.
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#include "RSA.h"
|
||||
#include "Key.h" //Key
|
||||
#include "KeyPair.h" //KeyPair
|
||||
#include "PrimeGenerator.h" //Generate()
|
||||
#include <string> //string
|
||||
#include <fstream> //ifstream, ofstream
|
||||
|
||||
using std::string;
|
||||
|
||||
/* Returns the greatest common divisor of the two arguments
|
||||
* "a" and "b", using the Euclidean algorithm. */
|
||||
BigInt RSA::GCD(const BigInt &a, const BigInt &b)
|
||||
{
|
||||
if (b.EqualsZero())
|
||||
return a;
|
||||
else
|
||||
return RSA::GCD(b, a % b);
|
||||
}
|
||||
|
||||
/* Solves the equation
|
||||
* d = ax + by
|
||||
* given a and b, and returns d, x and y by reference.
|
||||
* It uses the Extended Euclidean Algorithm */
|
||||
void RSA::extendedEuclideanAlgorithm( const BigInt &a, const BigInt &b,
|
||||
BigInt &d, BigInt &x, BigInt &y)
|
||||
{
|
||||
if (b.EqualsZero())
|
||||
{
|
||||
d = a;
|
||||
x = BigIntOne;
|
||||
y = BigIntZero;
|
||||
return;
|
||||
}
|
||||
RSA::extendedEuclideanAlgorithm(b, a % b, d, x, y);
|
||||
BigInt temp(x);
|
||||
x = y;
|
||||
y = temp - a / b * y;
|
||||
}
|
||||
|
||||
/* Solves the equation
|
||||
* ax is congruent to b (mod n),
|
||||
* given a, b and n finds x. */
|
||||
BigInt RSA::solveModularLinearEquation( const BigInt &a,
|
||||
const BigInt &b,
|
||||
const BigInt &n)
|
||||
{
|
||||
BigInt p, q, r;
|
||||
RSA::extendedEuclideanAlgorithm(a, n, p, q, r);
|
||||
if ((b % p).EqualsZero()) // This has to evaluate to 'true'.
|
||||
return (q * (b / p)) % n;
|
||||
else
|
||||
throw "Error RSA00: Error in key generation."; // Detect mistakes.
|
||||
}
|
||||
|
||||
/* Throws an exception if "key" is too short to be used. */
|
||||
void RSA::checkKeyLength(const Key &key)
|
||||
{
|
||||
// Minimum required key length is around 24 bits. (In-house requirement)
|
||||
if (key.GetModulus().Length() < 8)
|
||||
throw "Error RSA01: Keys must be at least 8 digits long.";
|
||||
}
|
||||
|
||||
/* Transforms a std::string message into a BigInt message.
|
||||
* Every ASCII character of the original message is replaced by it's
|
||||
* ASCII value and appended to the end of the newly created BigInt object
|
||||
* 'decoded' as a three-digit number, from left to right. */
|
||||
BigInt RSA::encode(const string &message)
|
||||
{
|
||||
// The new number will be created using a string object (encoded),
|
||||
// and converted into a BigInt on return.
|
||||
string encoded;
|
||||
encoded.resize(message.length() * 3 + 1);
|
||||
unsigned long int index = message.length() * 3;
|
||||
for (unsigned long int i(0); i < message.length(); i++)
|
||||
{
|
||||
// Encode the characters using their ASCII values' digits as
|
||||
// BigInt digits.
|
||||
unsigned char ASCII = message[i];
|
||||
encoded[index - 2] = (ASCII % 10) + '0';
|
||||
ASCII /= 10;
|
||||
encoded[index - 1] = (ASCII % 10) + '0';
|
||||
encoded[index] = (ASCII / 10) + '0';
|
||||
index -= 3;
|
||||
}
|
||||
// We add an special symbol '1' to the beginning of the string 'encoded'
|
||||
// to make sure that the returned BigInt doesn't begin with a zero. We also
|
||||
// need to make sure we remove that '1' when decoding (see RSA::decode()).
|
||||
encoded[0] = '1';
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/* Transforms a BigInt cyphertext into a std::string cyphertext. */
|
||||
string RSA::decode(const BigInt &message)
|
||||
{
|
||||
string decoded;
|
||||
// The special symbol '1' we added to the beginning of the encoded message
|
||||
// will now be positioned at message[message.Length() - 1], and
|
||||
// message.Length() -1 must be divisible by 3 without remainder. Thus we
|
||||
// can ignore the special symbol by only using digits in the range
|
||||
// from message[0] to message[message.Length() - 2].
|
||||
for (unsigned long int i(0); i < message.Length() / 3; i++)
|
||||
{
|
||||
// Decode the characters using the ASCII values in the BigInt digits.
|
||||
char ASCII = 100 * char(message.GetDigit(i * 3));
|
||||
ASCII += 10 * char(message.GetDigit(i * 3 + 1));
|
||||
decoded.push_back(ASCII + char(message.GetDigit(i * 3 + 2)));
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
/* Encrypts a "chunk" (a small part of a message) using "key" */
|
||||
string RSA::encryptChunk(const string &chunk, const Key &key)
|
||||
{
|
||||
// First encode the chunk, to make sure it is represented as an integer.
|
||||
BigInt a = RSA::encode(chunk);
|
||||
// The RSA encryption algorithm is a congruence equation.
|
||||
a.SetPowerMod(key.GetExponent(), key.GetModulus());
|
||||
return a.ToString();
|
||||
}
|
||||
|
||||
/* Decrypts a "chunk" (a small part of a message) using "key" */
|
||||
string RSA::decryptChunk(const BigInt &chunk, const Key &key)
|
||||
{
|
||||
BigInt a = chunk;
|
||||
// The RSA decryption algorithm is a congruence equation.
|
||||
a.SetPowerMod(key.GetExponent(), key.GetModulus());
|
||||
// Decode the message to a readable form.
|
||||
return RSA::decode(a);
|
||||
}
|
||||
|
||||
/* Encrypts a string "message" using "key". */
|
||||
std::string RSA::encryptString(const std::string &message, const Key &key)
|
||||
{
|
||||
//partition the message into biggest possible encryptable chunks
|
||||
const unsigned long int chunkSize(((key.GetModulus().Length() - 2) / 3));
|
||||
const unsigned long int chunkCount = message.length() / chunkSize;
|
||||
|
||||
string cypherText;
|
||||
for (unsigned long int i(0); i < chunkCount; i++)
|
||||
{
|
||||
// Get the next chunk.
|
||||
string chunk(message.substr(i * chunkSize, chunkSize));
|
||||
chunk = RSA::encryptChunk(chunk, key);
|
||||
// Put a ' ' between the chunks so that we can separate them later.
|
||||
cypherText.append(chunk.append(" "));
|
||||
}
|
||||
// If the last chunk has the same size as the others, we are finished.
|
||||
if (chunkSize * chunkCount == message.length())
|
||||
return cypherText;
|
||||
|
||||
// Handle the last chunk. It is smaller than the others.
|
||||
const unsigned long int lastChunkSize = message.length() % chunkSize;
|
||||
string lastChunk(message.substr(chunkCount * chunkSize, lastChunkSize));
|
||||
lastChunk = RSA::encryptChunk(lastChunk, key);
|
||||
return cypherText.append(lastChunk.append(" "));
|
||||
}
|
||||
|
||||
/* Decrypts a string "message" using "key". */
|
||||
std::string RSA::decryptString(const std::string &cypherText, const Key &key)
|
||||
{
|
||||
// Partition the cypherText into chunks. They are seperated by ' '.
|
||||
string message;
|
||||
long int i(0), j(0);
|
||||
while ((j = cypherText.find(' ', i)) != -1)
|
||||
{
|
||||
// Get the chunk.
|
||||
BigInt chunk(cypherText.substr(i, j - i));
|
||||
if (chunk >= key.GetModulus())
|
||||
throw "Error RSA02: Chunk too large.";
|
||||
|
||||
// Decrypt the chunk and store the message.
|
||||
string text = RSA::decryptChunk(chunk, key);
|
||||
message.append(text);
|
||||
i = j + 1;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/* Tests the file for 'eof', 'bad ' errors and throws an exception. */
|
||||
void RSA::fileError(bool eof, bool bad)
|
||||
{
|
||||
if (eof)
|
||||
throw "Error RSA03: Unexpected end of file.";
|
||||
else if (bad)
|
||||
throw "Error RSA04: Bad file?";
|
||||
else
|
||||
throw "Error RSA05: File contains unexpected data.";
|
||||
}
|
||||
|
||||
/* Returns the string "message" RSA-encrypted using the key "key". */
|
||||
string RSA::Encrypt(const string &message, const Key &key)
|
||||
{
|
||||
RSA::checkKeyLength(key);
|
||||
|
||||
return RSA::encryptString(message, key);
|
||||
}
|
||||
|
||||
/* Encrypts the file "sourceFile" using the key "key" and saves
|
||||
* the result into the file "destFile". */
|
||||
void RSA::Encrypt( const char *sourceFile, const char *destFile,
|
||||
const Key &key)
|
||||
{
|
||||
RSA::checkKeyLength(key);
|
||||
|
||||
//open the input and output files
|
||||
std::ifstream source(sourceFile, std::ios::in | std::ios::binary);
|
||||
if (!source)
|
||||
throw "Error RSA06: Opening file \"sourceFile\" failed.";
|
||||
std::ofstream dest(destFile, std::ios::out | std::ios::binary);
|
||||
if (!dest)
|
||||
throw "Error RSA07: Creating file \"destFile\" failed.";
|
||||
|
||||
//find the source file length
|
||||
source.seekg(0, std::ios::end);
|
||||
const unsigned long int fileSize = source.tellg();
|
||||
source.seekg(0, std::ios::beg);
|
||||
|
||||
//create an input buffer
|
||||
const unsigned long int bufferSize = 4096;
|
||||
char buffer[bufferSize];
|
||||
|
||||
//encrypt file chunks
|
||||
const unsigned long int chunkCount = fileSize / bufferSize;
|
||||
for (unsigned long int i(0); i <= chunkCount; i++)
|
||||
{
|
||||
unsigned long int readLength;
|
||||
//read the chunk
|
||||
if (i == chunkCount) //if it's the last one
|
||||
readLength = fileSize % bufferSize;
|
||||
else
|
||||
readLength = sizeof buffer;
|
||||
source.read(buffer, readLength);
|
||||
if (!source)
|
||||
RSA::fileError(source.eof(), source.bad());
|
||||
|
||||
//encrypt the chunk
|
||||
std::string chunk(buffer, readLength);
|
||||
chunk = RSA::encryptString(chunk, key);
|
||||
//write the chunk
|
||||
dest.write(chunk.c_str(), chunk.length());
|
||||
if (!dest)
|
||||
RSA::fileError(dest.eof(), dest.bad());
|
||||
}
|
||||
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
|
||||
/* Returns the string "cypherText" RSA-decrypted using the key "key". */
|
||||
string RSA::Decrypt(const string &cypherText, const Key &key)
|
||||
{
|
||||
RSA::checkKeyLength(key);
|
||||
|
||||
return RSA::decryptString(cypherText, key);
|
||||
}
|
||||
|
||||
/* Decrypts the file "sourceFile" using the key "key" and saves
|
||||
* the result into the file "destFile". */
|
||||
void RSA::Decrypt( const char *sourceFile, const char *destFile,
|
||||
const Key &key)
|
||||
{
|
||||
RSA::checkKeyLength(key);
|
||||
|
||||
//open the input and output files
|
||||
std::ifstream source(sourceFile, std::ios::in | std::ios::binary);
|
||||
if (!source)
|
||||
throw "Error RSA08: Opening file \"sourceFile\" failed.";
|
||||
std::ofstream dest(destFile, std::ios::out | std::ios::binary);
|
||||
if (!dest)
|
||||
throw "Error RSA09: Creating file \"destFile\" failed.";
|
||||
|
||||
//find the source file length
|
||||
source.seekg(0, std::ios::end);
|
||||
const unsigned long int fileSize = source.tellg();
|
||||
source.seekg(0, std::ios::beg);
|
||||
|
||||
//create an input buffer
|
||||
const unsigned long int bufferSize = 8192;
|
||||
char buffer[bufferSize];
|
||||
unsigned long int readCount = 0;
|
||||
|
||||
while (readCount < fileSize)
|
||||
{
|
||||
unsigned long int readLength;
|
||||
//read new data
|
||||
if (fileSize - readCount >= bufferSize) //if it's not the last one
|
||||
readLength = sizeof buffer;
|
||||
else
|
||||
readLength = fileSize - readCount;
|
||||
source.read(buffer, readLength);
|
||||
if (!source)
|
||||
RSA::fileError(source.eof(), source.bad());
|
||||
|
||||
//find the next chunk
|
||||
std::string chunk(buffer, readLength);
|
||||
chunk = chunk.substr(0, chunk.find_last_of(' ', chunk.length()) + 1);
|
||||
readCount += chunk.length();
|
||||
source.seekg(readCount, std::ios::beg);
|
||||
//decrypt the chunk
|
||||
chunk = RSA::decryptString(chunk, key);
|
||||
//write the chunk
|
||||
dest.write(chunk.c_str(), chunk.length());
|
||||
if (!dest)
|
||||
RSA::fileError(dest.eof(), dest.bad());
|
||||
}
|
||||
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
|
||||
/* Generates a public/private keypair. The keys are retured in a
|
||||
* KeyPair. The generated keys are 'digitCount' or
|
||||
* 'digitCount' + 1 digits long. */
|
||||
KeyPair RSA::GenerateKeyPair( unsigned long int digitCount,
|
||||
unsigned long int k)
|
||||
{
|
||||
if (digitCount < 8)
|
||||
throw "Error RSA10: Keys must be at least 8 digits long.";
|
||||
|
||||
//generate two random numbers p and q
|
||||
BigInt p(PrimeGenerator::Generate(digitCount / 2 + 2, k));
|
||||
BigInt q(PrimeGenerator::Generate(digitCount / 2 - 1, k));
|
||||
|
||||
//make sure they are different
|
||||
while (p == q)
|
||||
{
|
||||
p = PrimeGenerator::Generate(digitCount / 2 + 1, k);
|
||||
}
|
||||
|
||||
//calculate the modulus of both the public and private keys, n
|
||||
BigInt n(p * q);
|
||||
|
||||
//calculate the totient phi
|
||||
BigInt phi((p - BigIntOne) * (q - BigIntOne));
|
||||
|
||||
//select a small odd integer e that is coprime with phi and e < phi
|
||||
//usually 65537 is used, and we will use it too if it fits
|
||||
//it is recommended that this be the least possible value for e
|
||||
BigInt e("65537");
|
||||
|
||||
//make sure the requirements are met
|
||||
while (RSA::GCD(phi, e) != BigIntOne || e < "65537" || !e.IsOdd())
|
||||
{
|
||||
PrimeGenerator::MakeRandom(e, 5);
|
||||
}
|
||||
|
||||
//now we have enough information to create the public key
|
||||
//e is the public key exponent, n is the modulus
|
||||
Key publicKey(n, e);
|
||||
|
||||
//calculate d, d * e = 1 (mod phi)
|
||||
BigInt d(RSA::solveModularLinearEquation(e, BigIntOne, phi));
|
||||
|
||||
//we need a positive private exponent
|
||||
if (!d.IsPositive())
|
||||
return RSA::GenerateKeyPair(digitCount, k);
|
||||
|
||||
//we can create the private key
|
||||
//d is the private key exponent, n is the modulus
|
||||
Key privateKey(n, d);
|
||||
|
||||
//finally, the keypair is created and returned
|
||||
KeyPair newKeyPair(privateKey, publicKey);
|
||||
return newKeyPair;
|
||||
}
|
112
src/rsalib/RSA.h
112
src/rsalib/RSA.h
|
@ -1,112 +0,0 @@
|
|||
/* ****************************************************************************
|
||||
*
|
||||
* RSA.h
|
||||
*
|
||||
* Author: Nedim Srndic
|
||||
* Release date: 16th of June 2008
|
||||
*
|
||||
* An implementation of the RSA public-key cryptography algorithm.
|
||||
*
|
||||
* RSA supports:
|
||||
*
|
||||
* - Message encryption (string and file) (Encrypt())
|
||||
* - Message decryption (string and file) (Decrypt())
|
||||
* - Public/private keypair generation (GenerateKeyPair())
|
||||
*
|
||||
* NOTE: All methods are static. Instantiation, copying and assignment of
|
||||
* objects of type RSA is forbidden.
|
||||
*
|
||||
* NOTE: it is highly recommended to call
|
||||
* std::srand(time(NULL));
|
||||
* once when the program starts and before any use of methods provided by the
|
||||
* RSA class. Calling the srand() function randomizes the standard C++
|
||||
* pseudorandom number generator, so that it provides different series of
|
||||
* pseudorandom numbers every time the program is run. This greatly improves
|
||||
* security.
|
||||
*
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef RSA_H_
|
||||
#define RSA_H_
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include "KeyPair.h"
|
||||
#include "Key.h"
|
||||
#include "BigInt.h"
|
||||
|
||||
class RSA
|
||||
{
|
||||
private:
|
||||
/* Instantiation of objects of type RSA is forbidden. */
|
||||
RSA()
|
||||
{}
|
||||
/* Copying of objects of type RSA is forbidden. */
|
||||
RSA(const RSA &rsa);
|
||||
/* Assignment of objects of type RSA is forbidden. */
|
||||
RSA &operator=(const RSA &rsa);
|
||||
/* Returns the greatest common divisor of the two arguments
|
||||
* "a" and "b", using the Euclidean algorithm. */
|
||||
static BigInt GCD(const BigInt &a, const BigInt &b);
|
||||
/* Solves the equation
|
||||
* d = ax + by
|
||||
* given a and b, and returns d, x and y by reference.
|
||||
* It uses the Extended Euclidean Algorithm */
|
||||
static void extendedEuclideanAlgorithm( const BigInt &a,
|
||||
const BigInt &b,
|
||||
BigInt &d,
|
||||
BigInt &x,
|
||||
BigInt &y);
|
||||
/* Solves the equation
|
||||
* ax is congruent to b (mod n),
|
||||
* given a, b and n finds x. */
|
||||
static BigInt solveModularLinearEquation( const BigInt &a,
|
||||
const BigInt &b,
|
||||
const BigInt &n);
|
||||
/* Throws an exception if "key" is too short to be used. */
|
||||
static void checkKeyLength(const Key &key);
|
||||
/* Transforms a std::string message into a BigInt message. */
|
||||
static BigInt encode(const std::string &message);
|
||||
/* Transforms a BigInt cyphertext into a std::string cyphertext. */
|
||||
static std::string decode(const BigInt &message);
|
||||
/* Encrypts a "chunk" (a small part of a message) using "key" */
|
||||
static std::string encryptChunk(const std::string &chunk,
|
||||
const Key &key);
|
||||
/* Decrypts a "chunk" (a small part of a message) using "key" */
|
||||
static std::string decryptChunk(const BigInt &chunk,
|
||||
const Key &key);
|
||||
/* Encrypts a string "message" using "key". */
|
||||
static std::string encryptString( const std::string &message,
|
||||
const Key &key);
|
||||
/* Decrypts a string "message" using "key". */
|
||||
static std::string decryptString( const std::string &cypherText,
|
||||
const Key &key);
|
||||
/* Tests the file for 'eof', 'bad ' errors and throws an exception. */
|
||||
static void fileError(bool eof, bool bad);
|
||||
public:
|
||||
/* Returns the string "message" RSA-encrypted using the key "key". */
|
||||
static std::string Encrypt( const std::string &message,
|
||||
const Key &key);
|
||||
/* Encrypts the file "sourceFile" using the key "key" and saves
|
||||
* the result into the file "destFile". */
|
||||
static void Encrypt(const char *sourceFile,
|
||||
const char *destFile,
|
||||
const Key &key);
|
||||
/* Decrypts the file "sourceFile" using the key "key" and saves
|
||||
* the result into the file "destFile". */
|
||||
static void Decrypt(const char *sourceFile,
|
||||
const char *destFile,
|
||||
const Key &key);
|
||||
/* Returns the string "cypherText" RSA-decrypted
|
||||
* using the key "key". */
|
||||
static std::string Decrypt( const std::string &cypherText,
|
||||
const Key &key);
|
||||
/* Generates a public/private keypair. The keys are retured in a
|
||||
* KeyPair. The generated keys are 'digitCount' or
|
||||
* 'digitCount' + 1 digits long. */
|
||||
static KeyPair GenerateKeyPair( unsigned long int digitCount,
|
||||
unsigned long int k = 3);
|
||||
};
|
||||
|
||||
#endif /*RSA_H_*/
|
Reference in New Issue