810 lines
21 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|