// Returns gcd(this, bi) public RSABigInteger gcd(RSABigInteger bi) { RSABigInteger x; RSABigInteger y; if ((data[maxLength - 1] & 0x80000000) != 0) // negative { x = -this; } else { x = this; } if ((bi.data[maxLength - 1] & 0x80000000) != 0) // negative { y = -bi; } else { y = bi; } RSABigInteger g = y; while (x.dataLength > 1 || (x.dataLength == 1 && x.data[0] != 0)) { g = x; x = y % x; y = g; } return(g); }
// Overloading of unary >> operators public static RSABigInteger operator >>(RSABigInteger bi1, int shiftVal) { RSABigInteger result = new RSABigInteger(bi1); result.dataLength = shiftRight(result.data, shiftVal); if ((bi1.data[maxLength - 1] & 0x80000000) != 0) // negative { for (int i = maxLength - 1; i >= result.dataLength; i--) { result.data[i] = 0xFFFFFFFF; } uint mask = 0x80000000; for (int i = 0; i < 32; i++) { if ((result.data[result.dataLength - 1] & mask) != 0) { break; } result.data[result.dataLength - 1] |= mask; mask >>= 1; } result.dataLength = maxLength; } return(result); }
// Overloading of unary << operators public static RSABigInteger operator <<(RSABigInteger bi1, int shiftVal) { RSABigInteger result = new RSABigInteger(bi1); result.dataLength = shiftLeft(result.data, shiftVal); return(result); }
// Returns a string representing the RSABigInteger in sign-and-magnitude // format in the specified radix. public string ToString(int radix) { if (radix < 2 || radix > 36) { throw (new ArgumentException("Radix must be >= 2 and <= 36")); } string charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; string result = ""; RSABigInteger a = this; bool negative = false; if ((a.data[maxLength - 1] & 0x80000000) != 0) { negative = true; try { a = -a; } catch (Exception) { } } RSABigInteger quotient = new RSABigInteger(); RSABigInteger remainder = new RSABigInteger(); RSABigInteger biRadix = new RSABigInteger(radix); if (a.dataLength == 1 && a.data[0] == 0) { result = "0"; } else { while (a.dataLength > 1 || (a.dataLength == 1 && a.data[0] != 0)) { singleByteDivide(a, biRadix, quotient, remainder); if (remainder.data[0] < 10) { result = remainder.data[0] + result; } else { result = charSet[(int)remainder.data[0] - 10] + result; } a = quotient; } if (negative) { result = "-" + result; } } return(result); }
// Constructor (Default value provided by RSABigInteger) public RSABigInteger(RSABigInteger bi) { data = new uint[maxLength]; dataLength = bi.dataLength; for (int i = 0; i < dataLength; i++) { data[i] = bi.data[i]; } }
// Overloading of subtraction operator public static RSABigInteger operator -(RSABigInteger bi1, RSABigInteger bi2) { RSABigInteger result = new RSABigInteger(); result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; long carryIn = 0; for (int i = 0; i < result.dataLength; i++) { long diff; diff = (long)bi1.data[i] - (long)bi2.data[i] - carryIn; result.data[i] = (uint)(diff & 0xFFFFFFFF); if (diff < 0) { carryIn = 1; } else { carryIn = 0; } } // roll over to negative if (carryIn != 0) { for (int i = result.dataLength; i < maxLength; i++) { result.data[i] = 0xFFFFFFFF; } result.dataLength = maxLength; } // fixed in v1.03 to give correct datalength for a - (-b) while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } // overflow check int lastPos = maxLength - 1; if ((bi1.data[lastPos] & 0x80000000) != (bi2.data[lastPos] & 0x80000000) && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException()); } return(result); }
// Overloading of the unary -- operator public static RSABigInteger operator --(RSABigInteger bi1) { RSABigInteger result = new RSABigInteger(bi1); long val; bool carryIn = true; int index = 0; while (carryIn && index < maxLength) { val = (long)(result.data[index]); val--; result.data[index] = (uint)(val & 0xFFFFFFFF); if (val >= 0) { carryIn = false; } index++; } if (index > result.dataLength) { result.dataLength = index; } while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } // overflow check int lastPos = maxLength - 1; // overflow if initial value was -ve but -- caused a sign // change to positive. if ((bi1.data[lastPos] & 0x80000000) != 0 && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException("Underflow in --.")); } return(result); }
// Overloading of the NEGATE operator (2's complement) public static RSABigInteger operator -(RSABigInteger bi1) { // handle neg of zero separately since it'll cause an overflow // if we proceed. if (bi1.dataLength == 1 && bi1.data[0] == 0) { return(new RSABigInteger()); } RSABigInteger result = new RSABigInteger(bi1); // 1's complement for (int i = 0; i < maxLength; i++) { result.data[i] = (uint)(~(bi1.data[i])); } // add one to result of 1's complement long val, carry = 1; int index = 0; while (carry != 0 && index < maxLength) { val = (long)(result.data[index]); val++; result.data[index] = (uint)(val & 0xFFFFFFFF); carry = val >> 32; index++; } if ((bi1.data[maxLength - 1] & 0x80000000) == (result.data[maxLength - 1] & 0x80000000)) { throw (new ArithmeticException("Overflow in negation.\n")); } result.dataLength = maxLength; while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } return(result); }
public override bool Equals(object o) { RSABigInteger bi = (RSABigInteger)o; if (this.dataLength != bi.dataLength) { return(false); } for (int i = 0; i < this.dataLength; i++) { if (this.data[i] != bi.data[i]) { return(false); } } return(true); }
// Overloading of division operator public static RSABigInteger operator /(RSABigInteger bi1, RSABigInteger bi2) { RSABigInteger quotient = new RSABigInteger(); RSABigInteger remainder = new RSABigInteger(); int lastPos = maxLength - 1; bool divisorNeg = false, dividendNeg = false; if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative { bi1 = -bi1; dividendNeg = true; } if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative { bi2 = -bi2; divisorNeg = true; } if (bi1 < bi2) { return(quotient); } else { if (bi2.dataLength == 1) { singleByteDivide(bi1, bi2, quotient, remainder); } else { multiByteDivide(bi1, bi2, quotient, remainder); } if (dividendNeg != divisorNeg) { return(-quotient); } return(quotient); } }
// Overloading of the unary ++ operator public static RSABigInteger operator ++(RSABigInteger bi1) { RSABigInteger result = new RSABigInteger(bi1); long val, carry = 1; int index = 0; while (carry != 0 && index < maxLength) { val = (long)(result.data[index]); val++; result.data[index] = (uint)(val & 0xFFFFFFFF); carry = val >> 32; index++; } if (index > result.dataLength) { result.dataLength = index; } else { while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } } // overflow check int lastPos = maxLength - 1; // overflow if initial value was +ve but ++ caused a sign // change to negative. if ((bi1.data[lastPos] & 0x80000000) == 0 && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException("Overflow in ++.")); } return(result); }
// Overloading of addition operator public static RSABigInteger operator +(RSABigInteger bi1, RSABigInteger bi2) { RSABigInteger result = new RSABigInteger(); result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; long carry = 0; for (int i = 0; i < result.dataLength; i++) { long sum = (long)bi1.data[i] + (long)bi2.data[i] + carry; carry = sum >> 32; result.data[i] = (uint)(sum & 0xFFFFFFFF); } if (carry != 0 && result.dataLength < maxLength) { result.data[result.dataLength] = (uint)(carry); result.dataLength++; } while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } // overflow check int lastPos = maxLength - 1; if ((bi1.data[lastPos] & 0x80000000) == (bi2.data[lastPos] & 0x80000000) && (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) { throw (new ArithmeticException()); } return(result); }
// Private function that supports the division of two numbers with // a divisor that has only 1 digit. private static void singleByteDivide(RSABigInteger bi1, RSABigInteger bi2, RSABigInteger outQuotient, RSABigInteger outRemainder) { uint[] result = new uint[maxLength]; int resultPos = 0; // copy dividend to reminder for (int i = 0; i < maxLength; i++) { outRemainder.data[i] = bi1.data[i]; } outRemainder.dataLength = bi1.dataLength; while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) { outRemainder.dataLength--; } ulong divisor = (ulong)bi2.data[0]; int pos = outRemainder.dataLength - 1; ulong dividend = (ulong)outRemainder.data[pos]; if (dividend >= divisor) { ulong quotient = dividend / divisor; result[resultPos++] = (uint)quotient; outRemainder.data[pos] = (uint)(dividend % divisor); } pos--; while (pos >= 0) { //Console.WriteLine(pos); dividend = ((ulong)outRemainder.data[pos + 1] << 32) + (ulong)outRemainder.data[pos]; ulong quotient = dividend / divisor; result[resultPos++] = (uint)quotient; outRemainder.data[pos + 1] = 0; outRemainder.data[pos--] = (uint)(dividend % divisor); //Console.WriteLine(">>>> " + bi1); } outQuotient.dataLength = resultPos; int j = 0; for (int i = outQuotient.dataLength - 1; i >= 0; i--, j++) { outQuotient.data[j] = result[i]; } for (; j < maxLength; j++) { outQuotient.data[j] = 0; } while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) { outQuotient.dataLength--; } if (outQuotient.dataLength == 0) { outQuotient.dataLength = 1; } while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) { outRemainder.dataLength--; } }
// Private function that supports the division of two numbers with // a divisor that has more than 1 digit. private static void multiByteDivide(RSABigInteger bi1, RSABigInteger bi2, RSABigInteger outQuotient, RSABigInteger outRemainder) { uint[] result = new uint[maxLength]; int remainderLen = bi1.dataLength + 1; uint[] remainder = new uint[remainderLen]; uint mask = 0x80000000; uint val = bi2.data[bi2.dataLength - 1]; int shift = 0, resultPos = 0; while (mask != 0 && (val & mask) == 0) { shift++; mask >>= 1; } for (int i = 0; i < bi1.dataLength; i++) { remainder[i] = bi1.data[i]; } shiftLeft(remainder, shift); bi2 = bi2 << shift; int j = remainderLen - bi2.dataLength; int pos = remainderLen - 1; ulong firstDivisorByte = bi2.data[bi2.dataLength - 1]; ulong secondDivisorByte = bi2.data[bi2.dataLength - 2]; int divisorLen = bi2.dataLength + 1; uint[] dividendPart = new uint[divisorLen]; while (j > 0) { ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; //Console.WriteLine("dividend = {0}", dividend); ulong q_hat = dividend / firstDivisorByte; ulong r_hat = dividend % firstDivisorByte; //Console.WriteLine("q_hat = {0:X}, r_hat = {1:X}", q_hat, r_hat); bool done = false; while (!done) { done = true; if (q_hat == 0x100000000 || (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) { q_hat--; r_hat += firstDivisorByte; if (r_hat < 0x100000000) { done = false; } } } for (int h = 0; h < divisorLen; h++) { dividendPart[h] = remainder[pos - h]; } RSABigInteger kk = new RSABigInteger(dividendPart); RSABigInteger ss = bi2 * (long)q_hat; //Console.WriteLine("ss before = " + ss); while (ss > kk) { q_hat--; ss -= bi2; //Console.WriteLine(ss); } RSABigInteger yy = kk - ss; for (int h = 0; h < divisorLen; h++) { remainder[pos - h] = yy.data[bi2.dataLength - h]; } result[resultPos++] = (uint)q_hat; pos--; j--; } outQuotient.dataLength = resultPos; int y = 0; for (int x = outQuotient.dataLength - 1; x >= 0; x--, y++) { outQuotient.data[y] = result[x]; } for (; y < maxLength; y++) { outQuotient.data[y] = 0; } while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) { outQuotient.dataLength--; } if (outQuotient.dataLength == 0) { outQuotient.dataLength = 1; } outRemainder.dataLength = shiftRight(remainder, shift); for (y = 0; y < outRemainder.dataLength; y++) { outRemainder.data[y] = remainder[y]; } for (; y < maxLength; y++) { outRemainder.data[y] = 0; } }
// Modulo Exponentiation public RSABigInteger modPow(RSABigInteger exp, RSABigInteger n) { if ((exp.data[maxLength - 1] & 0x80000000) != 0) { throw (new ArithmeticException("Positive exponents only.")); } RSABigInteger resultNum = 1; RSABigInteger tempNum; bool thisNegative = false; if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative this { tempNum = -this % n; thisNegative = true; } else { tempNum = this % n; // ensures (tempNum * tempNum) < b^(2k) } if ((n.data[maxLength - 1] & 0x80000000) != 0) // negative n { n = -n; } // calculate constant = b^(2k) / m RSABigInteger constant = new RSABigInteger(); int i = n.dataLength << 1; constant.data[i] = 0x00000001; constant.dataLength = i + 1; constant = constant / n; int totalBits = exp.bitCount(); int count = 0; // perform squaring and multiply exponentiation for (int pos = 0; pos < exp.dataLength; pos++) { uint mask = 0x01; for (int index = 0; index < 32; index++) { if ((exp.data[pos] & mask) != 0) { resultNum = BarrettReduction(resultNum * tempNum, n, constant); } mask <<= 1; tempNum = BarrettReduction(tempNum * tempNum, n, constant); if (tempNum.dataLength == 1 && tempNum.data[0] == 1) { if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp { return(-resultNum); } return(resultNum); } count++; if (count == totalBits) { break; } } } if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp { return(-resultNum); } return(resultNum); }
// Overloading of multiplication operator public static RSABigInteger operator *(RSABigInteger bi1, RSABigInteger bi2) { int lastPos = maxLength - 1; bool bi1Neg = false, bi2Neg = false; // take the absolute value of the inputs try { if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative { bi1Neg = true; bi1 = -bi1; } if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative { bi2Neg = true; bi2 = -bi2; } } catch (Exception) { } RSABigInteger result = new RSABigInteger(); // multiply the absolute values try { for (int i = 0; i < bi1.dataLength; i++) { if (bi1.data[i] == 0) { continue; } ulong mcarry = 0; for (int j = 0, k = i; j < bi2.dataLength; j++, k++) { // k = i + j ulong val = ((ulong)bi1.data[i] * (ulong)bi2.data[j]) + (ulong)result.data[k] + mcarry; result.data[k] = (uint)(val & 0xFFFFFFFF); mcarry = (val >> 32); } if (mcarry != 0) { result.data[i + bi2.dataLength] = (uint)mcarry; } } } catch (Exception) { throw (new ArithmeticException("Multiplication overflow.")); } result.dataLength = bi1.dataLength + bi2.dataLength; if (result.dataLength > maxLength) { result.dataLength = maxLength; } while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) { result.dataLength--; } // overflow check (result is -ve) if ((result.data[lastPos] & 0x80000000) != 0) { if (bi1Neg != bi2Neg && result.data[lastPos] == 0x80000000) // different sign { // handle the special case where multiplication produces // a max negative number in 2's complement. if (result.dataLength == 1) { return(result); } else { bool isMaxNeg = true; for (int i = 0; i < result.dataLength - 1 && isMaxNeg; i++) { if (result.data[i] != 0) { isMaxNeg = false; } } if (isMaxNeg) { return(result); } } } throw (new ArithmeticException("Multiplication overflow.")); } // if input has different signs, then result is -ve if (bi1Neg != bi2Neg) { return(-result); } return(result); }
// Constructor (Default value provided by a string of digits of the // specified base) public RSABigInteger(string value, int radix) { RSABigInteger multiplier = new RSABigInteger(1); RSABigInteger result = new RSABigInteger(); value = (value.ToUpper()).Trim(); int limit = 0; if (value[0] == '-') { limit = 1; } for (int i = value.Length - 1; i >= limit; i--) { int posVal = (int)value[i]; if (posVal >= '0' && posVal <= '9') { posVal -= '0'; } else if (posVal >= 'A' && posVal <= 'Z') { posVal = (posVal - 'A') + 10; } else { posVal = 9999999; // arbitrary large } if (posVal >= radix) { throw (new ArithmeticException("Invalid string in constructor.")); } else { if (value[0] == '-') { posVal = -posVal; } result = result + (multiplier * posVal); if ((i - 1) >= limit) { multiplier = multiplier * radix; } } } if (value[0] == '-') // negative values { if ((result.data[maxLength - 1] & 0x80000000) == 0) { throw (new ArithmeticException("Negative underflow in constructor.")); } } else // positive values { if ((result.data[maxLength - 1] & 0x80000000) != 0) { throw (new ArithmeticException("Positive overflow in constructor.")); } } data = new uint[maxLength]; for (int i = 0; i < result.dataLength; i++) { data[i] = result.data[i]; } dataLength = result.dataLength; }
// Fast calculation of modular reduction using Barrett's reduction. // Requires x < b^(2k), where b is the base. In this case, base is // 2^32 (uint). private RSABigInteger BarrettReduction(RSABigInteger x, RSABigInteger n, RSABigInteger constant) { int k = n.dataLength, kPlusOne = k + 1, kMinusOne = k - 1; RSABigInteger q1 = new RSABigInteger(); for (int i = kMinusOne, j = 0; i < x.dataLength; i++, j++) { q1.data[j] = x.data[i]; } q1.dataLength = x.dataLength - kMinusOne; if (q1.dataLength <= 0) { q1.dataLength = 1; } RSABigInteger q2 = q1 * constant; RSABigInteger q3 = new RSABigInteger(); for (int i = kPlusOne, j = 0; i < q2.dataLength; i++, j++) { q3.data[j] = q2.data[i]; } q3.dataLength = q2.dataLength - kPlusOne; if (q3.dataLength <= 0) { q3.dataLength = 1; } RSABigInteger r1 = new RSABigInteger(); int lengthToCopy = (x.dataLength > kPlusOne) ? kPlusOne : x.dataLength; for (int i = 0; i < lengthToCopy; i++) { r1.data[i] = x.data[i]; } r1.dataLength = lengthToCopy; RSABigInteger r2 = new RSABigInteger(); for (int i = 0; i < q3.dataLength; i++) { if (q3.data[i] == 0) { continue; } ulong mcarry = 0; int t = i; for (int j = 0; j < n.dataLength && t < kPlusOne; j++, t++) { // t = i + j ulong val = ((ulong)q3.data[i] * (ulong)n.data[j]) + (ulong)r2.data[t] + mcarry; r2.data[t] = (uint)(val & 0xFFFFFFFF); mcarry = (val >> 32); } if (t < kPlusOne) { r2.data[t] = (uint)mcarry; } } r2.dataLength = kPlusOne; while (r2.dataLength > 1 && r2.data[r2.dataLength - 1] == 0) { r2.dataLength--; } r1 -= r2; if ((r1.data[maxLength - 1] & 0x80000000) != 0) // negative { RSABigInteger val = new RSABigInteger(); val.data[kPlusOne] = 0x00000001; val.dataLength = kPlusOne + 1; r1 += val; } while (r1 >= n) { r1 -= n; } return(r1); }