Files
hcs/Hcs.ClientNet/GostCryptography/Asn1/Ber/BigInteger.cs

810 lines
21 KiB
C#

using System;
using GostCryptography.Properties;
namespace GostCryptography.Asn1.Ber
{
[Serializable]
public class BigInteger
{
private const int AddressBits = 3;
private const int BitIndexMask = 7;
private const int BitsPerUnit = 8;
internal const int MaxBigIntLen = 100000;
private const int UnitMask = -1;
[NonSerialized]
private static readonly int[] BitsPerDigit =
{
0, 0, 0x400, 0x658, 0x800, 0x94a, 0xa58, 0xb3b, 0xc00, 0xcaf, 0xd4a, 0xdd7, 0xe58, 0xece, 0xf3b, 0xfa1,
0x1000, 0x105a, 0x10af, 0x10fe, 0x114a, 0x1192, 0x11d7, 0x1219, 0x1258, 0x1294, 0x12ce, 0x1306, 0x133b, 0x136f, 0x13a1, 0x13d2,
0x1400, 0x142e, 0x145a, 0x1485, 0x14af
};
[NonSerialized]
private static readonly int[] ByteRadix =
{
0, 0, 0x80, 0, 0, 0, 0, 0, 0x40, 0, 100, 0, 0, 0, 0, 0, 0x10
};
[NonSerialized]
private static readonly int[] DigitsPerByte =
{
0, 0, 7, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 1
};
private static readonly byte[] Zero = new byte[0];
[NonSerialized]
private int _sign;
[NonSerialized]
private byte[] _value;
public BigInteger()
{
_value = Zero;
_sign = 0;
}
public BigInteger(long value)
: this(value.ToString())
{
}
public BigInteger(string value)
{
Init(value, 10);
}
public BigInteger(byte[] value, int sign)
{
_value = value;
_sign = sign;
}
public BigInteger(string value, int radix)
{
Init(value, radix);
}
private static int BitsLeftOf(int x)
{
if (x != 0)
{
return (UnitMask << (BitsPerUnit - x));
}
return UnitMask;
}
private static void DestructiveMulAdd(byte[] x, int y, byte z)
{
var num = (byte)(y & 0xff);
var num2 = z;
var length = x.Length;
var num5 = 0;
for (var i = length - 1; i >= 0; i--)
{
var num4 = (num * x[i]) + num5;
x[i] = (byte)num4;
num5 = num4 >> BitsPerUnit;
}
var num7 = x[length - 1] + num2;
x[length - 1] = (byte)num7;
num5 = num7 >> BitsPerUnit;
for (var j = length - 2; j >= 0; j--)
{
num7 = x[j] + num5;
x[j] = (byte)num7;
num5 = num7 >> BitsPerUnit;
}
}
private static void DivideByInt(ref BigInteger divident, int divisor, ref BigInteger quotient, ref int reminder)
{
var index = 0;
var num3 = 4;
var num4 = 0;
var num5 = 0;
if (divisor == 0)
{
return;
}
reminder = 0;
if (divident._sign == 0)
{
quotient._sign = 0;
quotient._value = Zero;
return;
}
quotient._value = new byte[divident._value.Length];
var num2 = quotient._value.Length - 1;
quotient._sign = ((quotient._sign * divisor) > 0) ? 1 : -1;
var num6 = divident._value.Length * 2;
while (num4 < num6)
{
num5 = num5 << 4;
num4++;
num5 |= (divident._value[index] >> num3) & 15;
if (num3 == 0)
{
num3 = 4;
index++;
}
else
{
num3 = 0;
}
ShiftLeft(quotient, 4);
if (num5 >= divisor)
{
quotient._value[num2] = (byte)(quotient._value[num2] | ((byte)((num5 / divisor) & 15)));
num5 = num5 % divisor;
}
reminder = num5;
}
quotient._value = RemoveLeadingZeroBytes(quotient._value);
}
public bool Equals(long value)
{
return Equals(new BigInteger(value));
}
public override bool Equals(object value)
{
var integer = value as BigInteger;
if (integer == null)
{
return false;
}
if (_value.Length != integer._value.Length)
{
return false;
}
for (var i = 0; i < _value.Length; i++)
{
if (_value[i] != integer._value[i])
{
return false;
}
}
return true;
}
private static void FastCopy(ref BigInteger src, ref BigInteger dst)
{
dst._value = new byte[src._value.Length];
Array.Copy(src._value, 0, dst._value, 0, src._value.Length);
dst._sign = src._sign;
}
private BigInteger GetCopy()
{
var integer = new BigInteger();
if (_value.Length > 0)
{
integer._value = new byte[_value.Length];
Array.Copy(_value, 0, integer._value, 0, _value.Length);
}
else
{
integer._value = Zero;
}
integer._sign = _sign;
return integer;
}
private BigInteger GetCopyAndInverse()
{
var integer = new BigInteger();
if (_value.Length > 0)
{
integer._value = new byte[_value.Length];
if (_sign < 0)
{
integer._value = GetData();
integer._sign = 1;
return integer;
}
Array.Copy(_value, 0, integer._value, 0, _value.Length);
integer._sign = _sign;
return integer;
}
integer._value = Zero;
return integer;
}
public byte[] GetData()
{
int num2;
var dataLen = GetDataLen();
var index = _value.Length - 1;
var num4 = dataLen - 1;
if (_sign == 0)
{
return Zero;
}
var buffer = new byte[dataLen];
if (_sign >= 0)
{
num2 = _value.Length - 1;
while (((num2 >= 0) && (num4 >= 0)) && (index >= 0))
{
buffer[num4] = _value[index];
num2--;
num4--;
index--;
}
if ((dataLen - _value.Length) > 0)
{
buffer[num4] = 0;
}
return buffer;
}
num2 = _value.Length - 1;
while (((num2 >= 0) && (num4 >= 0)) && (index >= 0))
{
unchecked
{
buffer[num4] = (byte)-_value[index];
}
if (_value[index] != 0)
{
num2--;
num4--;
index--;
break;
}
num2--;
num4--;
index--;
}
while (((num2 >= 0) && (num4 >= 0)) && (index >= 0))
{
unchecked
{
buffer[num4] = (byte)~_value[index];
}
num2--;
num4--;
index--;
}
if ((dataLen - _value.Length) > 0)
{
buffer[num4] = 0xff;
}
return buffer;
}
private int GetDataLen()
{
if (_sign == 0)
{
return 1;
}
if ((_sign > 0) && ((_value[0] & 0x80) != 0))
{
return (_value.Length + 1);
}
if (_sign < 0)
{
var num = _value[0];
if ((_value.Length == 1) || ((_value.Length > 1) && (_value[1] == 0)))
{
num = (byte)~(num - 1);
}
else
{
unchecked
{
num = (byte)~num;
}
}
if ((num & 0x80) == 0)
{
return (_value.Length + 1);
}
}
return _value.Length;
}
public override int GetHashCode()
{
if (_value == null)
{
return base.GetHashCode();
}
if (_value.Length == 0)
{
return base.GetHashCode();
}
var num = 0;
var num2 = (_value.Length > 20) ? 20 : _value.Length;
for (var i = 0; i < num2; i++)
{
num ^= _value[i];
}
return num;
}
public void Init(string val, int radix)
{
var str = "";
if (val[0] == '-')
{
val = val.Substring(1);
str = "-";
}
if (val.StartsWith("0x"))
{
radix = 0x10;
val = val.Substring(2);
}
else if (val.StartsWith("0b"))
{
radix = 2;
val = val.Substring(2);
}
else if (val.StartsWith("0o"))
{
radix = 8;
val = val.Substring(2);
}
val = str + val;
var startIndex = 0;
var length = val.Length;
if (((radix != 2) && (radix != 0x10)) && ((radix != 10) && (radix != 8)))
{
throw new FormatException(Resources.Asn1InvalidFormatForBigIntegerValue);
}
if (val.Length == 0)
{
throw new FormatException(Resources.Asn1ZeroLengthBigInteger);
}
_sign = 1;
var index = val.IndexOf('-');
if (index != -1)
{
if (index != 0)
{
throw new FormatException(Resources.Asn1IllegalEmbeddedMinusSign);
}
if (val.Length == 1)
{
throw new FormatException(Resources.Asn1ZeroLengthBigInteger);
}
_sign = -1;
startIndex = 1;
}
while ((startIndex < length) && (val[startIndex] == '0'))
{
startIndex++;
}
if (startIndex == length)
{
_sign = 0;
_value = Zero;
}
else
{
var num2 = length - startIndex;
var num5 = Asn1Util.UrShift(num2 * BitsPerDigit[radix], 10) + 1;
var num1 = (num5 + 0x1f) / 0x20;
_value = new byte[num2];
var num6 = num2 % DigitsPerByte[radix];
if (num6 == 0)
{
num6 = DigitsPerByte[radix];
}
var str2 = val.Substring(startIndex, num6);
startIndex += num6;
_value[_value.Length - 1] = Convert.ToByte(str2, radix);
if (_value[_value.Length - 1] < 0)
{
throw new FormatException(Resources.Asn1IllegalDigit);
}
var y = ByteRadix[radix];
byte z;
while (startIndex < val.Length)
{
str2 = val.Substring(startIndex, DigitsPerByte[radix]);
startIndex += DigitsPerByte[radix];
z = Convert.ToByte(str2, radix);
if (z < 0)
{
throw new FormatException(Resources.Asn1IllegalDigit);
}
DestructiveMulAdd(_value, y, z);
}
_value = TrustedStripLeadingZeroInts(_value);
}
}
private static string IntToStr(long value, int radix)
{
var chArray = new char[0x22];
var num = 0;
var str = "";
if ((radix >= 2) && (radix <= 0x10))
{
while (num < 0x22)
{
chArray[num++] = (char)((ushort)(value % radix));
if ((value /= radix) == 0L)
{
break;
}
}
while (num != 0)
{
var ch = chArray[--num];
if (ch < '\n')
{
str = str + ((char)(ch + '0'));
}
else
{
str = str + ((char)((ch - '\n') + 0x41));
}
}
}
return str;
}
public bool IsNegative()
{
return (_sign < 0);
}
private char NibbleToHexChar(int b)
{
if ((b >= 0) && (b <= 9))
{
return (char)(b + 0x30);
}
if ((b >= 10) && (b <= 15))
{
return (char)((b - 10) + 0x61);
}
return '?';
}
public static implicit operator BigInteger(long value)
{
return new BigInteger(value);
}
private static byte[] RemoveLeadingZeroBytes(byte[] data)
{
if (data.Length == 0)
{
return data;
}
var index = 0;
while ((index < data.Length) && (data[index] == 0))
{
index++;
}
var destinationArray = new byte[data.Length - index];
Array.Copy(data, index, destinationArray, 0, data.Length - index);
return destinationArray;
}
public void SetData(byte[] ivalue)
{
if (ivalue.Length > MaxBigIntLen)
{
throw ExceptionUtility.CryptographicException(Resources.Asn1TooBigIntegerValue, ivalue.Length);
}
if ((ivalue.Length > 0) && ((ivalue[0] & 0x80) != 0))
{
var index = 0;
var num = 0;
_sign = -1;
while ((num < ivalue.Length) && (ivalue[index] == 0xff))
{
num++;
index++;
}
var num2 = num;
while ((num2 < ivalue.Length) && (ivalue[index] == 0))
{
num2++;
index++;
}
var num3 = (num2 == ivalue.Length) ? 1 : 0;
_value = new byte[(ivalue.Length - num) + num3];
index = num;
var num4 = num;
while (num < ivalue.Length)
{
unchecked
{
_value[(num - num4) + num3] = (byte)~ivalue[index];
}
num++;
index++;
}
for (num = _value.Length - 1; (_value[num] = (byte)(_value[num] + 1)) == 0; num--)
{
}
_value = RemoveLeadingZeroBytes(_value);
}
else
{
_value = RemoveLeadingZeroBytes(ivalue);
_sign = (ivalue.Length == 0) ? 0 : 1;
}
}
private static int ShiftLeft(BigInteger data, uint shift)
{
var value = data._value;
var length = value.Length;
var index = (int)(shift >> AddressBits);
var num3 = ((int)shift) & BitIndexMask;
var num4 = 8 - num3;
var num5 = 0;
var num7 = length;
if (length != 0)
{
length = length << AddressBits;
var num6 = (int)((((length - shift) + 8L) - 1L) >> AddressBits);
while (num5 < (num6 - 1))
{
value[num5++] = (byte)((value[index] << num3) | ((num4 == 8) ? 0 : (value[index + 1] >> num4)));
index++;
}
length &= BitIndexMask;
value[num5] = (num7 == num6) ? ((byte)((value[index] & BitsLeftOf(length)) << num3)) : ((byte)((value[index] << num3) | ((num4 == 8) ? 0 : ((value[index + 1] & BitsLeftOf(length)) >> num4))));
if (num6 < num7)
{
for (var i = num6; i < (num7 - num6); i++)
{
value[i] = 0;
}
}
}
return 0;
}
public override string ToString()
{
return ToString(10);
}
public string ToString(int radix)
{
if ((radix == 2) || (radix == 0x10))
{
int num;
int num2;
if (radix == 2)
{
num2 = 8;
num = 1;
}
else
{
num2 = 2;
num = 4;
}
var num3 = num2 * GetDataLen();
var chArray = new char[num3];
var index = num3 - 1;
for (var i = _value.Length - 1; i >= 0; i--)
{
byte num6;
int num8;
if (_sign < 0)
{
unchecked
{
num6 = (byte)~_value[i];
}
if ((_sign < 0) && ((num6 = (byte)(num6 + 1)) != 0))
{
_sign = 0;
}
}
else
{
num6 = _value[i];
}
var num7 = num8 = 0;
while (num7 < num2)
{
var b = (num6 >> num8) & ((1 << num) - 1);
chArray[index] = NibbleToHexChar(b);
num7++;
index--;
num8 += num;
}
}
while (index >= 0)
{
chArray[index--] = '0';
}
return new string(chArray);
}
var reminder = 0;
var str = "";
var quotient = new BigInteger();
var copy = (radix == 10) ? GetCopy() : GetCopyAndInverse();
do
{
DivideByInt(ref copy, ByteRadix[radix], ref quotient, ref reminder);
var str2 = IntToStr(reminder, radix);
var length = str2.Length;
str = str2 + str;
if ((quotient._value.Length != 0) || (radix != 10))
{
int num12;
for (num12 = length; num12 < DigitsPerByte[radix]; num12++)
{
str = '0' + str;
}
FastCopy(ref quotient, ref copy);
if (((quotient._value.Length == 0) && (_sign > 0)) && ((radix != 10) && ((reminder & 0x80) != 0)))
{
str = '0' + str;
}
}
else if ((_sign < 0) && (radix == 10))
{
str = '-' + str;
}
}
while ((quotient._value != null) && (quotient._value.Length != 0));
return str;
}
private static byte[] TrustedStripLeadingZeroInts(byte[] val)
{
var index = 0;
while ((index < val.Length) && (val[index] == 0))
{
index++;
}
if (index <= 0)
{
return val;
}
var buffer = new byte[val.Length - index];
for (var i = 0; i < (val.Length - index); i++)
{
buffer[i] = val[index + i];
}
return buffer;
}
}
}