diff --git a/SharpCompress.Test/SharpCompress.Test.Portable.csproj b/SharpCompress.Test/SharpCompress.Test.Portable.csproj
new file mode 100644
index 00000000..b1479253
--- /dev/null
+++ b/SharpCompress.Test/SharpCompress.Test.Portable.csproj
@@ -0,0 +1,113 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}
+ Library
+ Properties
+ SharpCompress.Test
+ SharpCompress.Test
+ v4.0
+
+
+ 512
+ ..\..\sharpcompress\
+ true
+ {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+ true
+
+
+ ..\SharpCompress\SharpCompress.pfx
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ SharpCompress.pfx
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ {efdcaf57-fd4d-4e5d-a3d5-f26b875817ed}
+ SharpCompress.PortableTest
+
+
+
+
+
\ No newline at end of file
diff --git a/SharpCompress.sln b/SharpCompress.sln
index b502ddfc..789058e4 100644
--- a/SharpCompress.sln
+++ b/SharpCompress.sln
@@ -9,6 +9,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCompress.Portable", "S
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCompress.WindowsStore", "SharpCompress\SharpCompress.WindowsStore.csproj", "{1DF6D83C-31FF-47B6-82FE-C4603BE916B5}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCompress.PortableTest", "SharpCompress\SharpCompress.PortableTest.csproj", "{EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpCompress.Test.Portable", "SharpCompress.Test\SharpCompress.Test.Portable.csproj", "{E9C3C94B-FB27-4B4F-B225-57513C254D37}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{932BBFCC-76E3-45FF-90CA-6BE4FBF4A097}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -67,8 +73,33 @@ Global
{1DF6D83C-31FF-47B6-82FE-C4603BE916B5}.Release|x64.Build.0 = Release|x64
{1DF6D83C-31FF-47B6-82FE-C4603BE916B5}.Release|x86.ActiveCfg = Release|x86
{1DF6D83C-31FF-47B6-82FE-C4603BE916B5}.Release|x86.Build.0 = Release|x86
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Release|ARM.ActiveCfg = Release|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Release|x64.ActiveCfg = Release|Any CPU
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED}.Release|x86.ActiveCfg = Release|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Release|ARM.ActiveCfg = Release|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Release|x64.ActiveCfg = Release|Any CPU
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {EFDCAF57-FD4D-4E5D-A3D5-F26B875817ED} = {932BBFCC-76E3-45FF-90CA-6BE4FBF4A097}
+ {15679D7A-F22C-4943-87FF-BF5C76C4A6FD} = {932BBFCC-76E3-45FF-90CA-6BE4FBF4A097}
+ {E9C3C94B-FB27-4B4F-B225-57513C254D37} = {932BBFCC-76E3-45FF-90CA-6BE4FBF4A097}
+ EndGlobalSection
EndGlobal
diff --git a/SharpCompress/Common/Zip/WinzipAesCryptoStream.Portable.cs b/SharpCompress/Common/Zip/WinzipAesCryptoStream.Portable.cs
new file mode 100644
index 00000000..1acbeefd
--- /dev/null
+++ b/SharpCompress/Common/Zip/WinzipAesCryptoStream.Portable.cs
@@ -0,0 +1,170 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace SharpCompress.Common.Zip
+{
+ internal class WinzipAesCryptoStream : Stream
+ {
+ private const int BLOCK_SIZE_IN_BYTES = 16;
+ private readonly IBufferedCipher rijndael;
+ private readonly byte[] counter = new byte[BLOCK_SIZE_IN_BYTES];
+ private readonly Stream stream;
+ private int nonce = 1;
+ private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES];
+ private bool isFinalBlock;
+ private long totalBytesLeftToRead;
+ private bool isDisposed;
+
+ internal WinzipAesCryptoStream(Stream stream, WinzipAesEncryptionData winzipAesEncryptionData, long length)
+ {
+ this.stream = stream;
+ totalBytesLeftToRead = length;
+
+ rijndael = CreateRijndael(winzipAesEncryptionData);
+ }
+
+ private IBufferedCipher CreateRijndael(WinzipAesEncryptionData winzipAesEncryptionData)
+ {
+ var blockCipher = new BufferedBlockCipher(new RijndaelEngine());
+ var param = new KeyParameter(winzipAesEncryptionData.KeyBytes);
+ blockCipher.Init(true, param);
+ return blockCipher;
+ }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public override long Length
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override long Position
+ {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (isDisposed)
+ {
+ return;
+ }
+ isDisposed = true;
+ if (disposing)
+ {
+ //read out last 10 auth bytes
+ var ten = new byte[10];
+ stream.Read(ten, 0, 10);
+ stream.Dispose();
+ }
+ }
+
+ public override void Flush()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (totalBytesLeftToRead == 0)
+ {
+ return 0;
+ }
+ int bytesToRead = count;
+ if (count > totalBytesLeftToRead)
+ {
+ bytesToRead = (int)totalBytesLeftToRead;
+ }
+ int read = stream.Read(buffer, offset, bytesToRead);
+ totalBytesLeftToRead -= read;
+
+ ReadTransformBlocks(buffer, offset, read);
+
+ return read;
+ }
+
+ private int ReadTransformOneBlock(byte[] buffer, int offset, int last)
+ {
+ if (isFinalBlock)
+ {
+ throw new InvalidOperationException();
+ }
+
+ int bytesRemaining = last - offset;
+ int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES)
+ ? BLOCK_SIZE_IN_BYTES
+ : bytesRemaining;
+
+ // update the counter
+ Array.Copy(BitConverter.GetBytes(nonce++), 0, counter, 0, 4);
+
+ // Determine if this is the final block
+ if ((bytesToRead == bytesRemaining) && (totalBytesLeftToRead == 0))
+ {
+ counterOut = rijndael.DoFinal(counter, 0, BLOCK_SIZE_IN_BYTES);
+
+ isFinalBlock = true;
+ }
+ else
+ {
+ rijndael.ProcessBytes(counter, 0, BLOCK_SIZE_IN_BYTES, counterOut, 0);
+ }
+ XorInPlace(buffer, offset, bytesToRead);
+ return bytesToRead;
+ }
+
+
+ private void XorInPlace(byte[] buffer, int offset, int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]);
+ }
+ }
+
+ private void ReadTransformBlocks(byte[] buffer, int offset, int count)
+ {
+ int posn = offset;
+ int last = count + offset;
+
+ while (posn < buffer.Length && posn < last)
+ {
+ int n = ReadTransformOneBlock(buffer, posn, last);
+ posn += n;
+ }
+ }
+
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs b/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs
index aef4d146..bc4fc484 100644
--- a/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs
+++ b/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs
@@ -9,49 +9,33 @@ namespace SharpCompress.Common.Zip
private const int BLOCK_SIZE_IN_BYTES = 16;
private readonly SymmetricAlgorithm cipher;
private readonly byte[] counter = new byte[BLOCK_SIZE_IN_BYTES];
- private readonly HMACSHA1 hmac;
private readonly Stream stream;
private readonly ICryptoTransform transform;
- private int _nonce = 1;
+ private int nonce = 1;
private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES];
private bool isFinalBlock;
- private CryptoMode mode;
private long totalBytesLeftToRead;
private bool isDisposed;
- internal WinzipAesCryptoStream(Stream stream, WinzipAesEncryptionData winzipAesEncryptionData, long length,
- CryptoMode mode)
+ internal WinzipAesCryptoStream(Stream stream, WinzipAesEncryptionData winzipAesEncryptionData, long length)
{
- this.mode = mode;
this.stream = stream;
totalBytesLeftToRead = length;
- hmac = new HMACSHA1(winzipAesEncryptionData.IvBytes);
-
cipher = CreateCipher(winzipAesEncryptionData);
var iv = new byte[BLOCK_SIZE_IN_BYTES];
transform = cipher.CreateEncryptor(winzipAesEncryptionData.KeyBytes, iv);
-
- //if (_mode == CryptoMode.Encrypt)
- //{
- // _iobuf = new byte[2048];
- // _PendingWriteBlock = new byte[BLOCK_SIZE_IN_BYTES];
- //}
}
private SymmetricAlgorithm CreateCipher(WinzipAesEncryptionData winzipAesEncryptionData)
{
-#if !PORTABLE
RijndaelManaged cipher = new RijndaelManaged();
cipher.BlockSize = BLOCK_SIZE_IN_BYTES * 8;
cipher.KeySize = winzipAesEncryptionData.KeyBytes.Length * 8;
cipher.Mode = CipherMode.ECB;
cipher.Padding = PaddingMode.None;
return cipher;
-#else
- throw new NotSupportedException("Cannot decrypt Winzip AES with Silverlight or WP7.");
-#endif
}
public override bool CanRead
@@ -133,12 +117,11 @@ namespace SharpCompress.Common.Zip
: bytesRemaining;
// update the counter
- Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4);
+ Array.Copy(BitConverter.GetBytes(nonce++), 0, counter, 0, 4);
// Determine if this is the final block
if ((bytesToRead == bytesRemaining) && (totalBytesLeftToRead == 0))
{
- hmac.TransformFinalBlock(buffer, offset, bytesToRead);
counterOut = transform.TransformFinalBlock(counter,
0,
BLOCK_SIZE_IN_BYTES);
@@ -146,7 +129,6 @@ namespace SharpCompress.Common.Zip
}
else
{
- hmac.TransformBlock(buffer, offset, bytesToRead, null, 0);
transform.TransformBlock(counter,
0, // offset
BLOCK_SIZE_IN_BYTES,
diff --git a/SharpCompress/Common/Zip/WinzipAesEncryptionData.Portable.cs b/SharpCompress/Common/Zip/WinzipAesEncryptionData.Portable.cs
new file mode 100644
index 00000000..0fe7050d
--- /dev/null
+++ b/SharpCompress/Common/Zip/WinzipAesEncryptionData.Portable.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Text;
+using SharpCompress.Crypto;
+
+namespace SharpCompress.Common.Zip
+{
+ internal class WinzipAesEncryptionData
+ {
+ private const int RFC2898_ITERATIONS = 1000;
+
+ private byte[] salt;
+ private WinzipAesKeySize keySize;
+ private byte[] passwordVerifyValue;
+ private string password;
+
+ private byte[] generatedVerifyValue;
+
+ internal WinzipAesEncryptionData(WinzipAesKeySize keySize, byte[] salt, byte[] passwordVerifyValue,
+ string password)
+ {
+ this.keySize = keySize;
+ this.salt = salt;
+ this.passwordVerifyValue = passwordVerifyValue;
+ this.password = password;
+ Initialize();
+ }
+
+ internal byte[] IvBytes { get; set; }
+ internal byte[] KeyBytes { get; set; }
+
+ private int KeySizeInBytes
+ {
+ get { return KeyLengthInBytes(keySize); }
+ }
+
+ internal static int KeyLengthInBytes(WinzipAesKeySize keySize)
+ {
+ switch (keySize)
+ {
+ case WinzipAesKeySize.KeySize128:
+ return 16;
+ case WinzipAesKeySize.KeySize192:
+ return 24;
+ case WinzipAesKeySize.KeySize256:
+ return 32;
+ }
+ throw new InvalidOperationException();
+ }
+
+ private void Initialize()
+ {
+ var utf8 = new UTF8Encoding(false);
+ var paramz = new PBKDF2(utf8.GetBytes(password), salt, RFC2898_ITERATIONS);
+ KeyBytes = paramz.GetBytes(KeySizeInBytes);
+ IvBytes = paramz.GetBytes(KeySizeInBytes);
+ generatedVerifyValue = paramz.GetBytes(2);
+
+
+ short verify = BitConverter.ToInt16(passwordVerifyValue, 0);
+ if (password != null)
+ {
+ short generated = BitConverter.ToInt16(generatedVerifyValue, 0);
+ if (verify != generated)
+ throw new InvalidFormatException("bad password");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpCompress/Common/Zip/ZipFilePart.cs b/SharpCompress/Common/Zip/ZipFilePart.cs
index 93f8d7ab..6052d593 100644
--- a/SharpCompress/Common/Zip/ZipFilePart.cs
+++ b/SharpCompress/Common/Zip/ZipFilePart.cs
@@ -89,7 +89,7 @@ namespace SharpCompress.Common.Zip
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
? -1
- : (long) Header.UncompressedSize);
+ : (long)Header.UncompressedSize);
}
case ZipCompressionMethod.PPMd:
{
@@ -120,7 +120,7 @@ namespace SharpCompress.Common.Zip
{
throw new InvalidFormatException("Unexpected vendor ID for WinZip AES metadata");
}
- Header.CompressionMethod = (ZipCompressionMethod) BitConverter.ToUInt16(data.DataBytes, 5);
+ Header.CompressionMethod = (ZipCompressionMethod)BitConverter.ToUInt16(data.DataBytes, 5);
return CreateDecompressionStream(stream);
}
default:
@@ -134,7 +134,7 @@ namespace SharpCompress.Common.Zip
{
if ((Header.CompressedSize == 0)
#if !PORTABLE && !NETFX_CORE
- && ((Header.PkwareTraditionalEncryptionData != null)
+ && ((Header.PkwareTraditionalEncryptionData != null)
|| (Header.WinzipAesEncryptionData != null)))
#else
&& (Header.PkwareTraditionalEncryptionData != null))
@@ -160,8 +160,7 @@ namespace SharpCompress.Common.Zip
if (Header.WinzipAesEncryptionData != null)
{
//only read 10 less because the last ten are auth bytes
- return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData,
- Header.CompressedSize - 10, CryptoMode.Decrypt);
+ return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10);
}
#endif
return plainStream;
diff --git a/SharpCompress/Crypto/BigInteger.cs b/SharpCompress/Crypto/BigInteger.cs
new file mode 100644
index 00000000..4cd6926d
--- /dev/null
+++ b/SharpCompress/Crypto/BigInteger.cs
@@ -0,0 +1,3145 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Text;
+
+
+namespace Org.BouncyCastle.Math
+{
+ public class BigInteger
+ {
+ // The primes b/w 2 and ~2^10
+ /*
+ 3 5 7 11 13 17 19 23 29
+ 31 37 41 43 47 53 59 61 67 71
+ 73 79 83 89 97 101 103 107 109 113
+ 127 131 137 139 149 151 157 163 167 173
+ 179 181 191 193 197 199 211 223 227 229
+ 233 239 241 251 257 263 269 271 277 281
+ 283 293 307 311 313 317 331 337 347 349
+ 353 359 367 373 379 383 389 397 401 409
+ 419 421 431 433 439 443 449 457 461 463
+ 467 479 487 491 499 503 509 521 523 541
+ 547 557 563 569 571 577 587 593 599 601
+ 607 613 617 619 631 641 643 647 653 659
+ 661 673 677 683 691 701 709 719 727 733
+ 739 743 751 757 761 769 773 787 797 809
+ 811 821 823 827 829 839 853 857 859 863
+ 877 881 883 887 907 911 919 929 937 941
+ 947 953 967 971 977 983 991 997
+ 1009 1013 1019 1021 1031
+ */
+
+ // Each list has a product < 2^31
+ private static readonly int[][] primeLists = new int[][]
+ {
+ new int[]{ 3, 5, 7, 11, 13, 17, 19, 23 },
+ new int[]{ 29, 31, 37, 41, 43 },
+ new int[]{ 47, 53, 59, 61, 67 },
+ new int[]{ 71, 73, 79, 83 },
+ new int[]{ 89, 97, 101, 103 },
+
+ new int[]{ 107, 109, 113, 127 },
+ new int[]{ 131, 137, 139, 149 },
+ new int[]{ 151, 157, 163, 167 },
+ new int[]{ 173, 179, 181, 191 },
+ new int[]{ 193, 197, 199, 211 },
+
+ new int[]{ 223, 227, 229 },
+ new int[]{ 233, 239, 241 },
+ new int[]{ 251, 257, 263 },
+ new int[]{ 269, 271, 277 },
+ new int[]{ 281, 283, 293 },
+
+ new int[]{ 307, 311, 313 },
+ new int[]{ 317, 331, 337 },
+ new int[]{ 347, 349, 353 },
+ new int[]{ 359, 367, 373 },
+ new int[]{ 379, 383, 389 },
+
+ new int[]{ 397, 401, 409 },
+ new int[]{ 419, 421, 431 },
+ new int[]{ 433, 439, 443 },
+ new int[]{ 449, 457, 461 },
+ new int[]{ 463, 467, 479 },
+
+ new int[]{ 487, 491, 499 },
+ new int[]{ 503, 509, 521 },
+ new int[]{ 523, 541, 547 },
+ new int[]{ 557, 563, 569 },
+ new int[]{ 571, 577, 587 },
+
+ new int[]{ 593, 599, 601 },
+ new int[]{ 607, 613, 617 },
+ new int[]{ 619, 631, 641 },
+ new int[]{ 643, 647, 653 },
+ new int[]{ 659, 661, 673 },
+
+ new int[]{ 677, 683, 691 },
+ new int[]{ 701, 709, 719 },
+ new int[]{ 727, 733, 739 },
+ new int[]{ 743, 751, 757 },
+ new int[]{ 761, 769, 773 },
+
+ new int[]{ 787, 797, 809 },
+ new int[]{ 811, 821, 823 },
+ new int[]{ 827, 829, 839 },
+ new int[]{ 853, 857, 859 },
+ new int[]{ 863, 877, 881 },
+
+ new int[]{ 883, 887, 907 },
+ new int[]{ 911, 919, 929 },
+ new int[]{ 937, 941, 947 },
+ new int[]{ 953, 967, 971 },
+ new int[]{ 977, 983, 991 },
+
+ new int[]{ 997, 1009, 1013 },
+ new int[]{ 1019, 1021, 1031 },
+ };
+
+ private static readonly int[] primeProducts;
+
+ private const long IMASK = 0xffffffffL;
+ private static readonly ulong UIMASK = (ulong)IMASK;
+
+ private static readonly int[] ZeroMagnitude = new int[0];
+ private static readonly byte[] ZeroEncoding = new byte[0];
+
+ public static readonly BigInteger Zero = new BigInteger(0, ZeroMagnitude, false);
+ public static readonly BigInteger One = createUValueOf(1);
+ public static readonly BigInteger Two = createUValueOf(2);
+ public static readonly BigInteger Three = createUValueOf(3);
+ public static readonly BigInteger Ten = createUValueOf(10);
+
+ private static readonly int chunk2 = 1; // TODO Parse 64 bits at a time
+ private static readonly BigInteger radix2 = ValueOf(2);
+ private static readonly BigInteger radix2E = radix2.Pow(chunk2);
+
+ private static readonly int chunk10 = 19;
+ private static readonly BigInteger radix10 = ValueOf(10);
+ private static readonly BigInteger radix10E = radix10.Pow(chunk10);
+
+ private static readonly int chunk16 = 16;
+ private static readonly BigInteger radix16 = ValueOf(16);
+ private static readonly BigInteger radix16E = radix16.Pow(chunk16);
+
+ private static readonly Random RandomSource = new Random();
+
+ private const int BitsPerByte = 8;
+ private const int BitsPerInt = 32;
+ private const int BytesPerInt = 4;
+
+ static BigInteger()
+ {
+ primeProducts = new int[primeLists.Length];
+
+ for (int i = 0; i < primeLists.Length; ++i)
+ {
+ int[] primeList = primeLists[i];
+ int product = 1;
+ for (int j = 0; j < primeList.Length; ++j)
+ {
+ product *= primeList[j];
+ }
+ primeProducts[i] = product;
+ }
+ }
+
+ private int sign; // -1 means -ve; +1 means +ve; 0 means 0;
+ private int[] magnitude; // array of ints with [0] being the most significant
+ private int nBits = -1; // cache BitCount() value
+ private int nBitLength = -1; // cache calcBitLength() value
+ private long mQuote = -1L; // -m^(-1) mod b, b = 2^32 (see Montgomery mult.)
+
+ private static int GetByteLength(
+ int nBits)
+ {
+ return (nBits + BitsPerByte - 1) / BitsPerByte;
+ }
+
+ private BigInteger()
+ {
+ }
+
+ private BigInteger(
+ int signum,
+ int[] mag,
+ bool checkMag)
+ {
+ if (checkMag)
+ {
+ int i = 0;
+ while (i < mag.Length && mag[i] == 0)
+ {
+ ++i;
+ }
+
+ if (i == mag.Length)
+ {
+ // this.sign = 0;
+ this.magnitude = ZeroMagnitude;
+ }
+ else
+ {
+ this.sign = signum;
+
+ if (i == 0)
+ {
+ this.magnitude = mag;
+ }
+ else
+ {
+ // strip leading 0 words
+ this.magnitude = new int[mag.Length - i];
+ Array.Copy(mag, i, this.magnitude, 0, this.magnitude.Length);
+ }
+ }
+ }
+ else
+ {
+ this.sign = signum;
+ this.magnitude = mag;
+ }
+ }
+
+ public BigInteger(
+ string value)
+ : this(value, 10)
+ {
+ }
+
+ public BigInteger(
+ string str,
+ int radix)
+ {
+ if (str.Length == 0)
+ throw new FormatException("Zero length BigInteger");
+
+ NumberStyles style;
+ int chunk;
+ BigInteger r;
+ BigInteger rE;
+
+ switch (radix)
+ {
+ case 2:
+ // Is there anyway to restrict to binary digits?
+ style = NumberStyles.Integer;
+ chunk = chunk2;
+ r = radix2;
+ rE = radix2E;
+ break;
+ case 10:
+ // This style seems to handle spaces and minus sign already (our processing redundant?)
+ style = NumberStyles.Integer;
+ chunk = chunk10;
+ r = radix10;
+ rE = radix10E;
+ break;
+ case 16:
+ // TODO Should this be HexNumber?
+ style = NumberStyles.AllowHexSpecifier;
+ chunk = chunk16;
+ r = radix16;
+ rE = radix16E;
+ break;
+ default:
+ throw new FormatException("Only bases 2, 10, or 16 allowed");
+ }
+
+
+ int index = 0;
+ sign = 1;
+
+ if (str[0] == '-')
+ {
+ if (str.Length == 1)
+ throw new FormatException("Zero length BigInteger");
+
+ sign = -1;
+ index = 1;
+ }
+
+ // strip leading zeros from the string str
+ while (index < str.Length && Int32.Parse(str[index].ToString(), style) == 0)
+ {
+ index++;
+ }
+
+ if (index >= str.Length)
+ {
+ // zero value - we're done
+ sign = 0;
+ magnitude = ZeroMagnitude;
+ return;
+ }
+
+ //////
+ // could we work out the max number of ints required to store
+ // str.Length digits in the given base, then allocate that
+ // storage in one hit?, then Generate the magnitude in one hit too?
+ //////
+
+ BigInteger b = Zero;
+
+
+ int next = index + chunk;
+
+ if (next <= str.Length)
+ {
+ do
+ {
+ string s = str.Substring(index, chunk);
+ ulong i = ulong.Parse(s, style);
+ BigInteger bi = createUValueOf(i);
+
+ switch (radix)
+ {
+ case 2:
+ // TODO Need this because we are parsing in radix 10 above
+ if (i > 1)
+ throw new FormatException("Bad character in radix 2 string: " + s);
+
+ // TODO Parse 64 bits at a time
+ b = b.ShiftLeft(1);
+ break;
+ case 16:
+ b = b.ShiftLeft(64);
+ break;
+ default:
+ b = b.Multiply(rE);
+ break;
+ }
+
+ b = b.Add(bi);
+
+ index = next;
+ next += chunk;
+ }
+ while (next <= str.Length);
+ }
+
+ if (index < str.Length)
+ {
+ string s = str.Substring(index);
+ ulong i = ulong.Parse(s, style);
+ BigInteger bi = createUValueOf(i);
+
+ if (b.sign > 0)
+ {
+ if (radix == 2)
+ {
+ // NB: Can't reach here since we are parsing one char at a time
+ Debug.Assert(false);
+
+ // TODO Parse all bits at once
+ // b = b.ShiftLeft(s.Length);
+ }
+ else if (radix == 16)
+ {
+ b = b.ShiftLeft(s.Length << 2);
+ }
+ else
+ {
+ b = b.Multiply(r.Pow(s.Length));
+ }
+
+ b = b.Add(bi);
+ }
+ else
+ {
+ b = bi;
+ }
+ }
+
+ // Note: This is the previous (slower) algorithm
+ // while (index < value.Length)
+ // {
+ // char c = value[index];
+ // string s = c.ToString();
+ // int i = Int32.Parse(s, style);
+ //
+ // b = b.Multiply(r).Add(ValueOf(i));
+ // index++;
+ // }
+
+ magnitude = b.magnitude;
+ }
+
+ public BigInteger(
+ byte[] bytes)
+ : this(bytes, 0, bytes.Length)
+ {
+ }
+
+ public BigInteger(
+ byte[] bytes,
+ int offset,
+ int length)
+ {
+ if (length == 0)
+ throw new FormatException("Zero length BigInteger");
+
+ // TODO Move this processing into MakeMagnitude (provide sign argument)
+ if ((sbyte)bytes[offset] < 0)
+ {
+ this.sign = -1;
+
+ int end = offset + length;
+
+ int iBval;
+ // strip leading sign bytes
+ for (iBval = offset; iBval < end && ((sbyte)bytes[iBval] == -1); iBval++)
+ {
+ }
+
+ if (iBval >= end)
+ {
+ this.magnitude = One.magnitude;
+ }
+ else
+ {
+ int numBytes = end - iBval;
+ byte[] inverse = new byte[numBytes];
+
+ int index = 0;
+ while (index < numBytes)
+ {
+ inverse[index++] = (byte)~bytes[iBval++];
+ }
+
+ Debug.Assert(iBval == end);
+
+ while (inverse[--index] == byte.MaxValue)
+ {
+ inverse[index] = byte.MinValue;
+ }
+
+ inverse[index]++;
+
+ this.magnitude = MakeMagnitude(inverse, 0, inverse.Length);
+ }
+ }
+ else
+ {
+ // strip leading zero bytes and return magnitude bytes
+ this.magnitude = MakeMagnitude(bytes, offset, length);
+ this.sign = this.magnitude.Length > 0 ? 1 : 0;
+ }
+ }
+
+ private static int[] MakeMagnitude(
+ byte[] bytes,
+ int offset,
+ int length)
+ {
+ int end = offset + length;
+
+ // strip leading zeros
+ int firstSignificant;
+ for (firstSignificant = offset; firstSignificant < end
+ && bytes[firstSignificant] == 0; firstSignificant++)
+ {
+ }
+
+ if (firstSignificant >= end)
+ {
+ return ZeroMagnitude;
+ }
+
+ int nInts = (end - firstSignificant + 3) / BytesPerInt;
+ int bCount = (end - firstSignificant) % BytesPerInt;
+ if (bCount == 0)
+ {
+ bCount = BytesPerInt;
+ }
+
+ if (nInts < 1)
+ {
+ return ZeroMagnitude;
+ }
+
+ int[] mag = new int[nInts];
+
+ int v = 0;
+ int magnitudeIndex = 0;
+ for (int i = firstSignificant; i < end; ++i)
+ {
+ v <<= 8;
+ v |= bytes[i] & 0xff;
+ bCount--;
+ if (bCount <= 0)
+ {
+ mag[magnitudeIndex] = v;
+ magnitudeIndex++;
+ bCount = BytesPerInt;
+ v = 0;
+ }
+ }
+
+ if (magnitudeIndex < mag.Length)
+ {
+ mag[magnitudeIndex] = v;
+ }
+
+ return mag;
+ }
+
+ public BigInteger(
+ int sign,
+ byte[] bytes)
+ : this(sign, bytes, 0, bytes.Length)
+ {
+ }
+
+ public BigInteger(
+ int sign,
+ byte[] bytes,
+ int offset,
+ int length)
+ {
+ if (sign < -1 || sign > 1)
+ throw new FormatException("Invalid sign value");
+
+ if (sign == 0)
+ {
+ //this.sign = 0;
+ this.magnitude = ZeroMagnitude;
+ }
+ else
+ {
+ // copy bytes
+ this.magnitude = MakeMagnitude(bytes, offset, length);
+ this.sign = this.magnitude.Length < 1 ? 0 : sign;
+ }
+ }
+
+ public BigInteger(
+ int sizeInBits,
+ Random random)
+ {
+ if (sizeInBits < 0)
+ throw new ArgumentException("sizeInBits must be non-negative");
+
+ this.nBits = -1;
+ this.nBitLength = -1;
+
+ if (sizeInBits == 0)
+ {
+ // this.sign = 0;
+ this.magnitude = ZeroMagnitude;
+ return;
+ }
+
+ int nBytes = GetByteLength(sizeInBits);
+ byte[] b = new byte[nBytes];
+ random.NextBytes(b);
+
+ // strip off any excess bits in the MSB
+ b[0] &= rndMask[BitsPerByte * nBytes - sizeInBits];
+
+ this.magnitude = MakeMagnitude(b, 0, b.Length);
+ this.sign = this.magnitude.Length < 1 ? 0 : 1;
+ }
+
+ private static readonly byte[] rndMask = { 255, 127, 63, 31, 15, 7, 3, 1 };
+
+ public BigInteger(
+ int bitLength,
+ int certainty,
+ Random random)
+ {
+ if (bitLength < 2)
+ throw new ArithmeticException("bitLength < 2");
+
+ this.sign = 1;
+ this.nBitLength = bitLength;
+
+ if (bitLength == 2)
+ {
+ this.magnitude = random.Next(2) == 0
+ ? Two.magnitude
+ : Three.magnitude;
+ return;
+ }
+
+ int nBytes = GetByteLength(bitLength);
+ byte[] b = new byte[nBytes];
+
+ int xBits = BitsPerByte * nBytes - bitLength;
+ byte mask = rndMask[xBits];
+
+ for (; ; )
+ {
+ random.NextBytes(b);
+
+ // strip off any excess bits in the MSB
+ b[0] &= mask;
+
+ // ensure the leading bit is 1 (to meet the strength requirement)
+ b[0] |= (byte)(1 << (7 - xBits));
+
+ // ensure the trailing bit is 1 (i.e. must be odd)
+ b[nBytes - 1] |= 1;
+
+ this.magnitude = MakeMagnitude(b, 0, b.Length);
+ this.nBits = -1;
+ this.mQuote = -1L;
+
+ if (certainty < 1)
+ break;
+
+ if (CheckProbablePrime(certainty, random))
+ break;
+
+ if (bitLength > 32)
+ {
+ for (int rep = 0; rep < 10000; ++rep)
+ {
+ int n = 33 + random.Next(bitLength - 2);
+ this.magnitude[this.magnitude.Length - (n >> 5)] ^= (1 << (n & 31));
+ this.magnitude[this.magnitude.Length - 1] ^= ((random.Next() + 1) << 1);
+ this.mQuote = -1L;
+
+ if (CheckProbablePrime(certainty, random))
+ return;
+ }
+ }
+ }
+ }
+
+ public BigInteger Abs()
+ {
+ return sign >= 0 ? this : Negate();
+ }
+
+ /**
+ * return a = a + b - b preserved.
+ */
+ private static int[] AddMagnitudes(
+ int[] a,
+ int[] b)
+ {
+ int tI = a.Length - 1;
+ int vI = b.Length - 1;
+ long m = 0;
+
+ while (vI >= 0)
+ {
+ m += ((long)(uint)a[tI] + (long)(uint)b[vI--]);
+ a[tI--] = (int)m;
+ m = (long)((ulong)m >> 32);
+ }
+
+ if (m != 0)
+ {
+ while (tI >= 0 && ++a[tI--] == 0)
+ {
+ }
+ }
+
+ return a;
+ }
+
+ public BigInteger Add(
+ BigInteger value)
+ {
+ if (this.sign == 0)
+ return value;
+
+ if (this.sign != value.sign)
+ {
+ if (value.sign == 0)
+ return this;
+
+ if (value.sign < 0)
+ return Subtract(value.Negate());
+
+ return value.Subtract(Negate());
+ }
+
+ return AddToMagnitude(value.magnitude);
+ }
+
+ private BigInteger AddToMagnitude(
+ int[] magToAdd)
+ {
+ int[] big, small;
+ if (this.magnitude.Length < magToAdd.Length)
+ {
+ big = magToAdd;
+ small = this.magnitude;
+ }
+ else
+ {
+ big = this.magnitude;
+ small = magToAdd;
+ }
+
+ // Conservatively avoid over-allocation when no overflow possible
+ uint limit = uint.MaxValue;
+ if (big.Length == small.Length)
+ limit -= (uint)small[0];
+
+ bool possibleOverflow = (uint)big[0] >= limit;
+
+ int[] bigCopy;
+ if (possibleOverflow)
+ {
+ bigCopy = new int[big.Length + 1];
+ big.CopyTo(bigCopy, 1);
+ }
+ else
+ {
+ bigCopy = (int[])big.Clone();
+ }
+
+ bigCopy = AddMagnitudes(bigCopy, small);
+
+ return new BigInteger(this.sign, bigCopy, possibleOverflow);
+ }
+
+ public BigInteger And(
+ BigInteger value)
+ {
+ if (this.sign == 0 || value.sign == 0)
+ {
+ return Zero;
+ }
+
+ int[] aMag = this.sign > 0
+ ? this.magnitude
+ : Add(One).magnitude;
+
+ int[] bMag = value.sign > 0
+ ? value.magnitude
+ : value.Add(One).magnitude;
+
+ bool resultNeg = sign < 0 && value.sign < 0;
+ int resultLength = System.Math.Max(aMag.Length, bMag.Length);
+ int[] resultMag = new int[resultLength];
+
+ int aStart = resultMag.Length - aMag.Length;
+ int bStart = resultMag.Length - bMag.Length;
+
+ for (int i = 0; i < resultMag.Length; ++i)
+ {
+ int aWord = i >= aStart ? aMag[i - aStart] : 0;
+ int bWord = i >= bStart ? bMag[i - bStart] : 0;
+
+ if (this.sign < 0)
+ {
+ aWord = ~aWord;
+ }
+
+ if (value.sign < 0)
+ {
+ bWord = ~bWord;
+ }
+
+ resultMag[i] = aWord & bWord;
+
+ if (resultNeg)
+ {
+ resultMag[i] = ~resultMag[i];
+ }
+ }
+
+ BigInteger result = new BigInteger(1, resultMag, true);
+
+ // TODO Optimise this case
+ if (resultNeg)
+ {
+ result = result.Not();
+ }
+
+ return result;
+ }
+
+ public BigInteger AndNot(
+ BigInteger val)
+ {
+ return And(val.Not());
+ }
+
+ public int BitCount
+ {
+ get
+ {
+ if (nBits == -1)
+ {
+ if (sign < 0)
+ {
+ // TODO Optimise this case
+ nBits = Not().BitCount;
+ }
+ else
+ {
+ int sum = 0;
+ for (int i = 0; i < magnitude.Length; i++)
+ {
+ sum += bitCounts[(byte)magnitude[i]];
+ sum += bitCounts[(byte)(magnitude[i] >> 8)];
+ sum += bitCounts[(byte)(magnitude[i] >> 16)];
+ sum += bitCounts[(byte)(magnitude[i] >> 24)];
+ }
+ nBits = sum;
+ }
+ }
+
+ return nBits;
+ }
+ }
+
+ private readonly static byte[] bitCounts =
+ {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1,
+ 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4,
+ 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3,
+ 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
+ 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2,
+ 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3,
+ 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6,
+ 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,
+ 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5,
+ 6, 6, 7, 6, 7, 7, 8
+ };
+
+ private int calcBitLength(
+ int indx,
+ int[] mag)
+ {
+ for (; ; )
+ {
+ if (indx >= mag.Length)
+ return 0;
+
+ if (mag[indx] != 0)
+ break;
+
+ ++indx;
+ }
+
+ // bit length for everything after the first int
+ int bitLength = 32 * ((mag.Length - indx) - 1);
+
+ // and determine bitlength of first int
+ int firstMag = mag[indx];
+ bitLength += BitLen(firstMag);
+
+ // Check for negative powers of two
+ if (sign < 0 && ((firstMag & -firstMag) == firstMag))
+ {
+ do
+ {
+ if (++indx >= mag.Length)
+ {
+ --bitLength;
+ break;
+ }
+ }
+ while (mag[indx] == 0);
+ }
+
+ return bitLength;
+ }
+
+ public int BitLength
+ {
+ get
+ {
+ if (nBitLength == -1)
+ {
+ nBitLength = sign == 0
+ ? 0
+ : calcBitLength(0, magnitude);
+ }
+
+ return nBitLength;
+ }
+ }
+
+ //
+ // BitLen(value) is the number of bits in value.
+ //
+ private static int BitLen(
+ int w)
+ {
+ // Binary search - decision tree (5 tests, rarely 6)
+ return (w < 1 << 15 ? (w < 1 << 7
+ ? (w < 1 << 3 ? (w < 1 << 1
+ ? (w < 1 << 0 ? (w < 0 ? 32 : 0) : 1)
+ : (w < 1 << 2 ? 2 : 3)) : (w < 1 << 5
+ ? (w < 1 << 4 ? 4 : 5)
+ : (w < 1 << 6 ? 6 : 7)))
+ : (w < 1 << 11
+ ? (w < 1 << 9 ? (w < 1 << 8 ? 8 : 9) : (w < 1 << 10 ? 10 : 11))
+ : (w < 1 << 13 ? (w < 1 << 12 ? 12 : 13) : (w < 1 << 14 ? 14 : 15)))) : (w < 1 << 23 ? (w < 1 << 19
+ ? (w < 1 << 17 ? (w < 1 << 16 ? 16 : 17) : (w < 1 << 18 ? 18 : 19))
+ : (w < 1 << 21 ? (w < 1 << 20 ? 20 : 21) : (w < 1 << 22 ? 22 : 23))) : (w < 1 << 27
+ ? (w < 1 << 25 ? (w < 1 << 24 ? 24 : 25) : (w < 1 << 26 ? 26 : 27))
+ : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31)))));
+ }
+
+ // private readonly static byte[] bitLengths =
+ // {
+ // 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+ // 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ // 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ // 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ // 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
+ // 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ // 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ // 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ // 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ // 8, 8, 8, 8, 8, 8, 8, 8
+ // };
+
+ private bool QuickPow2Check()
+ {
+ return sign > 0 && nBits == 1;
+ }
+
+ public int CompareTo(
+ object obj)
+ {
+ return CompareTo((BigInteger)obj);
+ }
+
+ /**
+ * unsigned comparison on two arrays - note the arrays may
+ * start with leading zeros.
+ */
+ private static int CompareTo(
+ int xIndx,
+ int[] x,
+ int yIndx,
+ int[] y)
+ {
+ while (xIndx != x.Length && x[xIndx] == 0)
+ {
+ xIndx++;
+ }
+
+ while (yIndx != y.Length && y[yIndx] == 0)
+ {
+ yIndx++;
+ }
+
+ return CompareNoLeadingZeroes(xIndx, x, yIndx, y);
+ }
+
+ private static int CompareNoLeadingZeroes(
+ int xIndx,
+ int[] x,
+ int yIndx,
+ int[] y)
+ {
+ int diff = (x.Length - y.Length) - (xIndx - yIndx);
+
+ if (diff != 0)
+ {
+ return diff < 0 ? -1 : 1;
+ }
+
+ // lengths of magnitudes the same, test the magnitude values
+
+ while (xIndx < x.Length)
+ {
+ uint v1 = (uint)x[xIndx++];
+ uint v2 = (uint)y[yIndx++];
+
+ if (v1 != v2)
+ return v1 < v2 ? -1 : 1;
+ }
+
+ return 0;
+ }
+
+ public int CompareTo(
+ BigInteger value)
+ {
+ return sign < value.sign ? -1
+ : sign > value.sign ? 1
+ : sign == 0 ? 0
+ : sign * CompareNoLeadingZeroes(0, magnitude, 0, value.magnitude);
+ }
+
+ /**
+ * return z = x / y - done in place (z value preserved, x contains the
+ * remainder)
+ */
+ private int[] Divide(
+ int[] x,
+ int[] y)
+ {
+ int xStart = 0;
+ while (xStart < x.Length && x[xStart] == 0)
+ {
+ ++xStart;
+ }
+
+ int yStart = 0;
+ while (yStart < y.Length && y[yStart] == 0)
+ {
+ ++yStart;
+ }
+
+ Debug.Assert(yStart < y.Length);
+
+ int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+ int[] count;
+
+ if (xyCmp > 0)
+ {
+ int yBitLength = calcBitLength(yStart, y);
+ int xBitLength = calcBitLength(xStart, x);
+ int shift = xBitLength - yBitLength;
+
+ int[] iCount;
+ int iCountStart = 0;
+
+ int[] c;
+ int cStart = 0;
+ int cBitLength = yBitLength;
+ if (shift > 0)
+ {
+ // iCount = ShiftLeft(One.magnitude, shift);
+ iCount = new int[(shift >> 5) + 1];
+ iCount[0] = 1 << (shift % 32);
+
+ c = ShiftLeft(y, shift);
+ cBitLength += shift;
+ }
+ else
+ {
+ iCount = new int[] { 1 };
+
+ int len = y.Length - yStart;
+ c = new int[len];
+ Array.Copy(y, yStart, c, 0, len);
+ }
+
+ count = new int[iCount.Length];
+
+ for (; ; )
+ {
+ if (cBitLength < xBitLength
+ || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0)
+ {
+ Subtract(xStart, x, cStart, c);
+ AddMagnitudes(count, iCount);
+
+ while (x[xStart] == 0)
+ {
+ if (++xStart == x.Length)
+ return count;
+ }
+
+ //xBitLength = calcBitLength(xStart, x);
+ xBitLength = 32 * (x.Length - xStart - 1) + BitLen(x[xStart]);
+
+ if (xBitLength <= yBitLength)
+ {
+ if (xBitLength < yBitLength)
+ return count;
+
+ xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+
+ if (xyCmp <= 0)
+ break;
+ }
+ }
+
+ shift = cBitLength - xBitLength;
+
+ // NB: The case where c[cStart] is 1-bit is harmless
+ if (shift == 1)
+ {
+ uint firstC = (uint)c[cStart] >> 1;
+ uint firstX = (uint)x[xStart];
+ if (firstC > firstX)
+ ++shift;
+ }
+
+ if (shift < 2)
+ {
+ ShiftRightOneInPlace(cStart, c);
+ --cBitLength;
+ ShiftRightOneInPlace(iCountStart, iCount);
+ }
+ else
+ {
+ ShiftRightInPlace(cStart, c, shift);
+ cBitLength -= shift;
+ ShiftRightInPlace(iCountStart, iCount, shift);
+ }
+
+ //cStart = c.Length - ((cBitLength + 31) / 32);
+ while (c[cStart] == 0)
+ {
+ ++cStart;
+ }
+
+ while (iCount[iCountStart] == 0)
+ {
+ ++iCountStart;
+ }
+ }
+ }
+ else
+ {
+ count = new int[1];
+ }
+
+ if (xyCmp == 0)
+ {
+ AddMagnitudes(count, One.magnitude);
+ Array.Clear(x, xStart, x.Length - xStart);
+ }
+
+ return count;
+ }
+
+ public BigInteger Divide(
+ BigInteger val)
+ {
+ if (val.sign == 0)
+ throw new ArithmeticException("Division by zero error");
+
+ if (sign == 0)
+ return Zero;
+
+ if (val.QuickPow2Check()) // val is power of two
+ {
+ BigInteger result = this.Abs().ShiftRight(val.Abs().BitLength - 1);
+ return val.sign == this.sign ? result : result.Negate();
+ }
+
+ int[] mag = (int[])this.magnitude.Clone();
+
+ return new BigInteger(this.sign * val.sign, Divide(mag, val.magnitude), true);
+ }
+
+ public BigInteger[] DivideAndRemainder(
+ BigInteger val)
+ {
+ if (val.sign == 0)
+ throw new ArithmeticException("Division by zero error");
+
+ BigInteger[] biggies = new BigInteger[2];
+
+ if (sign == 0)
+ {
+ biggies[0] = Zero;
+ biggies[1] = Zero;
+ }
+ else if (val.QuickPow2Check()) // val is power of two
+ {
+ int e = val.Abs().BitLength - 1;
+ BigInteger quotient = this.Abs().ShiftRight(e);
+ int[] remainder = this.LastNBits(e);
+
+ biggies[0] = val.sign == this.sign ? quotient : quotient.Negate();
+ biggies[1] = new BigInteger(this.sign, remainder, true);
+ }
+ else
+ {
+ int[] remainder = (int[])this.magnitude.Clone();
+ int[] quotient = Divide(remainder, val.magnitude);
+
+ biggies[0] = new BigInteger(this.sign * val.sign, quotient, true);
+ biggies[1] = new BigInteger(this.sign, remainder, true);
+ }
+
+ return biggies;
+ }
+
+ public override bool Equals(
+ object obj)
+ {
+ if (obj == this)
+ return true;
+
+ BigInteger biggie = obj as BigInteger;
+ if (biggie == null)
+ return false;
+
+ if (biggie.sign != sign || biggie.magnitude.Length != magnitude.Length)
+ return false;
+
+ for (int i = 0; i < magnitude.Length; i++)
+ {
+ if (biggie.magnitude[i] != magnitude[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public BigInteger Gcd(
+ BigInteger value)
+ {
+ if (value.sign == 0)
+ return Abs();
+
+ if (sign == 0)
+ return value.Abs();
+
+ BigInteger r;
+ BigInteger u = this;
+ BigInteger v = value;
+
+ while (v.sign != 0)
+ {
+ r = u.Mod(v);
+ u = v;
+ v = r;
+ }
+
+ return u;
+ }
+
+ public override int GetHashCode()
+ {
+ int hc = magnitude.Length;
+ if (magnitude.Length > 0)
+ {
+ hc ^= magnitude[0];
+
+ if (magnitude.Length > 1)
+ {
+ hc ^= magnitude[magnitude.Length - 1];
+ }
+ }
+
+ return sign < 0 ? ~hc : hc;
+ }
+
+ // TODO Make public?
+ private BigInteger Inc()
+ {
+ if (this.sign == 0)
+ return One;
+
+ if (this.sign < 0)
+ return new BigInteger(-1, doSubBigLil(this.magnitude, One.magnitude), true);
+
+ return AddToMagnitude(One.magnitude);
+ }
+
+ public int IntValue
+ {
+ get
+ {
+ return sign == 0 ? 0
+ : sign > 0 ? magnitude[magnitude.Length - 1]
+ : -magnitude[magnitude.Length - 1];
+ }
+ }
+
+ /**
+ * return whether or not a BigInteger is probably prime with a
+ * probability of 1 - (1/2)**certainty.
+ *
From Knuth Vol 2, pg 395.
+ */
+ public bool IsProbablePrime(
+ int certainty)
+ {
+ if (certainty <= 0)
+ return true;
+
+ BigInteger n = Abs();
+
+ if (!n.TestBit(0))
+ return n.Equals(Two);
+
+ if (n.Equals(One))
+ return false;
+
+ return n.CheckProbablePrime(certainty, RandomSource);
+ }
+
+ private bool CheckProbablePrime(
+ int certainty,
+ Random random)
+ {
+ Debug.Assert(certainty > 0);
+ Debug.Assert(CompareTo(Two) > 0);
+ Debug.Assert(TestBit(0));
+
+
+ // Try to reduce the penalty for really small numbers
+ int numLists = System.Math.Min(BitLength - 1, primeLists.Length);
+
+ for (int i = 0; i < numLists; ++i)
+ {
+ int test = Remainder(primeProducts[i]);
+
+ int[] primeList = primeLists[i];
+ for (int j = 0; j < primeList.Length; ++j)
+ {
+ int prime = primeList[j];
+ int qRem = test % prime;
+ if (qRem == 0)
+ {
+ // We may find small numbers in the list
+ return BitLength < 16 && IntValue == prime;
+ }
+ }
+ }
+
+
+ // TODO Special case for < 10^16 (RabinMiller fixed list)
+ // if (BitLength < 30)
+ // {
+ // RabinMiller against 2, 3, 5, 7, 11, 13, 23 is sufficient
+ // }
+
+
+ // TODO Is it worth trying to create a hybrid of these two?
+ return RabinMillerTest(certainty, random);
+ // return SolovayStrassenTest(certainty, random);
+
+ // bool rbTest = RabinMillerTest(certainty, random);
+ // bool ssTest = SolovayStrassenTest(certainty, random);
+ //
+ // Debug.Assert(rbTest == ssTest);
+ //
+ // return rbTest;
+ }
+
+ internal bool RabinMillerTest(
+ int certainty,
+ Random random)
+ {
+ Debug.Assert(certainty > 0);
+ Debug.Assert(BitLength > 2);
+ Debug.Assert(TestBit(0));
+
+ // let n = 1 + d . 2^s
+ BigInteger n = this;
+ BigInteger nMinusOne = n.Subtract(One);
+ int s = nMinusOne.GetLowestSetBit();
+ BigInteger r = nMinusOne.ShiftRight(s);
+
+ Debug.Assert(s >= 1);
+
+ do
+ {
+ // TODO Make a method for random BigIntegers in range 0 < x < n)
+ // - Method can be optimized by only replacing examined bits at each trial
+ BigInteger a;
+ do
+ {
+ a = new BigInteger(n.BitLength, random);
+ }
+ while (a.CompareTo(One) <= 0 || a.CompareTo(nMinusOne) >= 0);
+
+ BigInteger y = a.ModPow(r, n);
+
+ if (!y.Equals(One))
+ {
+ int j = 0;
+ while (!y.Equals(nMinusOne))
+ {
+ if (++j == s)
+ return false;
+
+ y = y.ModPow(Two, n);
+
+ if (y.Equals(One))
+ return false;
+ }
+ }
+
+ certainty -= 2; // composites pass for only 1/4 possible 'a'
+ }
+ while (certainty > 0);
+
+ return true;
+ }
+
+ // private bool SolovayStrassenTest(
+ // int certainty,
+ // Random random)
+ // {
+ // Debug.Assert(certainty > 0);
+ // Debug.Assert(CompareTo(Two) > 0);
+ // Debug.Assert(TestBit(0));
+ //
+ // BigInteger n = this;
+ // BigInteger nMinusOne = n.Subtract(One);
+ // BigInteger e = nMinusOne.ShiftRight(1);
+ //
+ // do
+ // {
+ // BigInteger a;
+ // do
+ // {
+ // a = new BigInteger(nBitLength, random);
+ // }
+ // // NB: Spec says 0 < x < n, but 1 is trivial
+ // while (a.CompareTo(One) <= 0 || a.CompareTo(n) >= 0);
+ //
+ //
+ // // TODO Check this is redundant given the way Jacobi() works?
+ //// if (!a.Gcd(n).Equals(One))
+ //// return false;
+ //
+ // int x = Jacobi(a, n);
+ //
+ // if (x == 0)
+ // return false;
+ //
+ // BigInteger check = a.ModPow(e, n);
+ //
+ // if (x == 1 && !check.Equals(One))
+ // return false;
+ //
+ // if (x == -1 && !check.Equals(nMinusOne))
+ // return false;
+ //
+ // --certainty;
+ // }
+ // while (certainty > 0);
+ //
+ // return true;
+ // }
+ //
+ // private static int Jacobi(
+ // BigInteger a,
+ // BigInteger b)
+ // {
+ // Debug.Assert(a.sign >= 0);
+ // Debug.Assert(b.sign > 0);
+ // Debug.Assert(b.TestBit(0));
+ // Debug.Assert(a.CompareTo(b) < 0);
+ //
+ // int totalS = 1;
+ // for (;;)
+ // {
+ // if (a.sign == 0)
+ // return 0;
+ //
+ // if (a.Equals(One))
+ // break;
+ //
+ // int e = a.GetLowestSetBit();
+ //
+ // int bLsw = b.magnitude[b.magnitude.Length - 1];
+ // if ((e & 1) != 0 && ((bLsw & 7) == 3 || (bLsw & 7) == 5))
+ // totalS = -totalS;
+ //
+ // // TODO Confirm this is faster than later a1.Equals(One) test
+ // if (a.BitLength == e + 1)
+ // break;
+ // BigInteger a1 = a.ShiftRight(e);
+ //// if (a1.Equals(One))
+ //// break;
+ //
+ // int a1Lsw = a1.magnitude[a1.magnitude.Length - 1];
+ // if ((bLsw & 3) == 3 && (a1Lsw & 3) == 3)
+ // totalS = -totalS;
+ //
+ //// a = b.Mod(a1);
+ // a = b.Remainder(a1);
+ // b = a1;
+ // }
+ // return totalS;
+ // }
+
+ public long LongValue
+ {
+ get
+ {
+ if (sign == 0)
+ return 0;
+
+ long v;
+ if (magnitude.Length > 1)
+ {
+ v = ((long)magnitude[magnitude.Length - 2] << 32)
+ | (magnitude[magnitude.Length - 1] & IMASK);
+ }
+ else
+ {
+ v = (magnitude[magnitude.Length - 1] & IMASK);
+ }
+
+ return sign < 0 ? -v : v;
+ }
+ }
+
+ public BigInteger Max(
+ BigInteger value)
+ {
+ return CompareTo(value) > 0 ? this : value;
+ }
+
+ public BigInteger Min(
+ BigInteger value)
+ {
+ return CompareTo(value) < 0 ? this : value;
+ }
+
+ public BigInteger Mod(
+ BigInteger m)
+ {
+ if (m.sign < 1)
+ throw new ArithmeticException("Modulus must be positive");
+
+ BigInteger biggie = Remainder(m);
+
+ return (biggie.sign >= 0 ? biggie : biggie.Add(m));
+ }
+
+ public BigInteger ModInverse(
+ BigInteger m)
+ {
+ if (m.sign < 1)
+ throw new ArithmeticException("Modulus must be positive");
+
+ // TODO Too slow at the moment
+ // // "Fast Key Exchange with Elliptic Curve Systems" R.Schoeppel
+ // if (m.TestBit(0))
+ // {
+ // //The Almost Inverse Algorithm
+ // int k = 0;
+ // BigInteger B = One, C = Zero, F = this, G = m, tmp;
+ //
+ // for (;;)
+ // {
+ // // While F is even, do F=F/u, C=C*u, k=k+1.
+ // int zeroes = F.GetLowestSetBit();
+ // if (zeroes > 0)
+ // {
+ // F = F.ShiftRight(zeroes);
+ // C = C.ShiftLeft(zeroes);
+ // k += zeroes;
+ // }
+ //
+ // // If F = 1, then return B,k.
+ // if (F.Equals(One))
+ // {
+ // BigInteger half = m.Add(One).ShiftRight(1);
+ // BigInteger halfK = half.ModPow(BigInteger.ValueOf(k), m);
+ // return B.Multiply(halfK).Mod(m);
+ // }
+ //
+ // if (F.CompareTo(G) < 0)
+ // {
+ // tmp = G; G = F; F = tmp;
+ // tmp = B; B = C; C = tmp;
+ // }
+ //
+ // F = F.Add(G);
+ // B = B.Add(C);
+ // }
+ // }
+
+ BigInteger x = new BigInteger();
+ BigInteger gcd = ExtEuclid(this.Mod(m), m, x, null);
+
+ if (!gcd.Equals(One))
+ throw new ArithmeticException("Numbers not relatively prime.");
+
+ if (x.sign < 0)
+ {
+ x.sign = 1;
+ //x = m.Subtract(x);
+ x.magnitude = doSubBigLil(m.magnitude, x.magnitude);
+ }
+
+ return x;
+ }
+
+ /**
+ * Calculate the numbers u1, u2, and u3 such that:
+ *
+ * u1 * a + u2 * b = u3
+ *
+ * where u3 is the greatest common divider of a and b.
+ * a and b using the extended Euclid algorithm (refer p. 323
+ * of The Art of Computer Programming vol 2, 2nd ed).
+ * This also seems to have the side effect of calculating
+ * some form of multiplicative inverse.
+ *
+ * @param a First number to calculate gcd for
+ * @param b Second number to calculate gcd for
+ * @param u1Out the return object for the u1 value
+ * @param u2Out the return object for the u2 value
+ * @return The greatest common divisor of a and b
+ */
+ private static BigInteger ExtEuclid(
+ BigInteger a,
+ BigInteger b,
+ BigInteger u1Out,
+ BigInteger u2Out)
+ {
+ BigInteger u1 = One;
+ BigInteger u3 = a;
+ BigInteger v1 = Zero;
+ BigInteger v3 = b;
+
+ while (v3.sign > 0)
+ {
+ BigInteger[] q = u3.DivideAndRemainder(v3);
+
+ BigInteger tmp = v1.Multiply(q[0]);
+ BigInteger tn = u1.Subtract(tmp);
+ u1 = v1;
+ v1 = tn;
+
+ u3 = v3;
+ v3 = q[1];
+ }
+
+ if (u1Out != null)
+ {
+ u1Out.sign = u1.sign;
+ u1Out.magnitude = u1.magnitude;
+ }
+
+ if (u2Out != null)
+ {
+ BigInteger tmp = u1.Multiply(a);
+ tmp = u3.Subtract(tmp);
+ BigInteger res = tmp.Divide(b);
+ u2Out.sign = res.sign;
+ u2Out.magnitude = res.magnitude;
+ }
+
+ return u3;
+ }
+
+ private static void ZeroOut(
+ int[] x)
+ {
+ Array.Clear(x, 0, x.Length);
+ }
+
+ public BigInteger ModPow(
+ BigInteger exponent,
+ BigInteger m)
+ {
+ if (m.sign < 1)
+ throw new ArithmeticException("Modulus must be positive");
+
+ if (m.Equals(One))
+ return Zero;
+
+ if (exponent.sign == 0)
+ return One;
+
+ if (sign == 0)
+ return Zero;
+
+ int[] zVal = null;
+ int[] yAccum = null;
+ int[] yVal;
+
+ // Montgomery exponentiation is only possible if the modulus is odd,
+ // but AFAIK, this is always the case for crypto algo's
+ bool useMonty = ((m.magnitude[m.magnitude.Length - 1] & 1) == 1);
+ long mQ = 0;
+ if (useMonty)
+ {
+ mQ = m.GetMQuote();
+
+ // tmp = this * R mod m
+ BigInteger tmp = ShiftLeft(32 * m.magnitude.Length).Mod(m);
+ zVal = tmp.magnitude;
+
+ useMonty = (zVal.Length <= m.magnitude.Length);
+
+ if (useMonty)
+ {
+ yAccum = new int[m.magnitude.Length + 1];
+ if (zVal.Length < m.magnitude.Length)
+ {
+ int[] longZ = new int[m.magnitude.Length];
+ zVal.CopyTo(longZ, longZ.Length - zVal.Length);
+ zVal = longZ;
+ }
+ }
+ }
+
+ if (!useMonty)
+ {
+ if (magnitude.Length <= m.magnitude.Length)
+ {
+ //zAccum = new int[m.magnitude.Length * 2];
+ zVal = new int[m.magnitude.Length];
+ magnitude.CopyTo(zVal, zVal.Length - magnitude.Length);
+ }
+ else
+ {
+ //
+ // in normal practice we'll never see this...
+ //
+ BigInteger tmp = Remainder(m);
+
+ //zAccum = new int[m.magnitude.Length * 2];
+ zVal = new int[m.magnitude.Length];
+ tmp.magnitude.CopyTo(zVal, zVal.Length - tmp.magnitude.Length);
+ }
+
+ yAccum = new int[m.magnitude.Length * 2];
+ }
+
+ yVal = new int[m.magnitude.Length];
+
+ //
+ // from LSW to MSW
+ //
+ for (int i = 0; i < exponent.magnitude.Length; i++)
+ {
+ int v = exponent.magnitude[i];
+ int bits = 0;
+
+ if (i == 0)
+ {
+ while (v > 0)
+ {
+ v <<= 1;
+ bits++;
+ }
+
+ //
+ // first time in initialise y
+ //
+ zVal.CopyTo(yVal, 0);
+
+ v <<= 1;
+ bits++;
+ }
+
+ while (v != 0)
+ {
+ if (useMonty)
+ {
+ // Montgomery square algo doesn't exist, and a normal
+ // square followed by a Montgomery reduction proved to
+ // be almost as heavy as a Montgomery mulitply.
+ MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ);
+ }
+ else
+ {
+ Square(yAccum, yVal);
+ Remainder(yAccum, m.magnitude);
+ Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length);
+ ZeroOut(yAccum);
+ }
+ bits++;
+
+ if (v < 0)
+ {
+ if (useMonty)
+ {
+ MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ);
+ }
+ else
+ {
+ Multiply(yAccum, yVal, zVal);
+ Remainder(yAccum, m.magnitude);
+ Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0,
+ yVal.Length);
+ ZeroOut(yAccum);
+ }
+ }
+
+ v <<= 1;
+ }
+
+ while (bits < 32)
+ {
+ if (useMonty)
+ {
+ MultiplyMonty(yAccum, yVal, yVal, m.magnitude, mQ);
+ }
+ else
+ {
+ Square(yAccum, yVal);
+ Remainder(yAccum, m.magnitude);
+ Array.Copy(yAccum, yAccum.Length - yVal.Length, yVal, 0, yVal.Length);
+ ZeroOut(yAccum);
+ }
+ bits++;
+ }
+ }
+
+ if (useMonty)
+ {
+ // Return y * R^(-1) mod m by doing y * 1 * R^(-1) mod m
+ ZeroOut(zVal);
+ zVal[zVal.Length - 1] = 1;
+ MultiplyMonty(yAccum, yVal, zVal, m.magnitude, mQ);
+ }
+
+ BigInteger result = new BigInteger(1, yVal, true);
+
+ return exponent.sign > 0
+ ? result
+ : result.ModInverse(m);
+ }
+
+ /**
+ * return w with w = x * x - w is assumed to have enough space.
+ */
+ private static int[] Square(
+ int[] w,
+ int[] x)
+ {
+ // Note: this method allows w to be only (2 * x.Length - 1) words if result will fit
+ // if (w.Length != 2 * x.Length)
+ // throw new ArgumentException("no I don't think so...");
+
+ ulong u1, u2, c;
+
+ int wBase = w.Length - 1;
+
+ for (int i = x.Length - 1; i != 0; i--)
+ {
+ ulong v = (ulong)(uint)x[i];
+
+ u1 = v * v;
+ u2 = u1 >> 32;
+ u1 = (uint)u1;
+
+ u1 += (ulong)(uint)w[wBase];
+
+ w[wBase] = (int)(uint)u1;
+ c = u2 + (u1 >> 32);
+
+ for (int j = i - 1; j >= 0; j--)
+ {
+ --wBase;
+ u1 = v * (ulong)(uint)x[j];
+ u2 = u1 >> 31; // multiply by 2!
+ u1 = (uint)(u1 << 1); // multiply by 2!
+ u1 += c + (ulong)(uint)w[wBase];
+
+ w[wBase] = (int)(uint)u1;
+ c = u2 + (u1 >> 32);
+ }
+
+ c += (ulong)(uint)w[--wBase];
+ w[wBase] = (int)(uint)c;
+
+ if (--wBase >= 0)
+ {
+ w[wBase] = (int)(uint)(c >> 32);
+ }
+ else
+ {
+ Debug.Assert((uint)(c >> 32) == 0);
+ }
+ wBase += i;
+ }
+
+ u1 = (ulong)(uint)x[0];
+ u1 = u1 * u1;
+ u2 = u1 >> 32;
+ u1 = u1 & IMASK;
+
+ u1 += (ulong)(uint)w[wBase];
+
+ w[wBase] = (int)(uint)u1;
+ if (--wBase >= 0)
+ {
+ w[wBase] = (int)(uint)(u2 + (u1 >> 32) + (ulong)(uint)w[wBase]);
+ }
+ else
+ {
+ Debug.Assert((uint)(u2 + (u1 >> 32)) == 0);
+ }
+
+ return w;
+ }
+
+ /**
+ * return x with x = y * z - x is assumed to have enough space.
+ */
+ private static int[] Multiply(
+ int[] x,
+ int[] y,
+ int[] z)
+ {
+ int i = z.Length;
+
+ if (i < 1)
+ return x;
+
+ int xBase = x.Length - y.Length;
+
+ for (; ; )
+ {
+ long a = z[--i] & IMASK;
+ long val = 0;
+
+ for (int j = y.Length - 1; j >= 0; j--)
+ {
+ val += a * (y[j] & IMASK) + (x[xBase + j] & IMASK);
+
+ x[xBase + j] = (int)val;
+
+ val = (long)((ulong)val >> 32);
+ }
+
+ --xBase;
+
+ if (i < 1)
+ {
+ if (xBase >= 0)
+ {
+ x[xBase] = (int)val;
+ }
+ else
+ {
+ Debug.Assert(val == 0);
+ }
+ break;
+ }
+
+ x[xBase] = (int)val;
+ }
+
+ return x;
+ }
+
+ private static long FastExtEuclid(
+ long a,
+ long b,
+ long[] uOut)
+ {
+ long u1 = 1;
+ long u3 = a;
+ long v1 = 0;
+ long v3 = b;
+
+ while (v3 > 0)
+ {
+ long q, tn;
+
+ q = u3 / v3;
+
+ tn = u1 - (v1 * q);
+ u1 = v1;
+ v1 = tn;
+
+ tn = u3 - (v3 * q);
+ u3 = v3;
+ v3 = tn;
+ }
+
+ uOut[0] = u1;
+ uOut[1] = (u3 - (u1 * a)) / b;
+
+ return u3;
+ }
+
+ private static long FastModInverse(
+ long v,
+ long m)
+ {
+ if (m < 1)
+ throw new ArithmeticException("Modulus must be positive");
+
+ long[] x = new long[2];
+ long gcd = FastExtEuclid(v, m, x);
+
+ if (gcd != 1)
+ throw new ArithmeticException("Numbers not relatively prime.");
+
+ if (x[0] < 0)
+ {
+ x[0] += m;
+ }
+
+ return x[0];
+ }
+
+ // private static BigInteger MQuoteB = One.ShiftLeft(32);
+ // private static BigInteger MQuoteBSub1 = MQuoteB.Subtract(One);
+
+ /**
+ * Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size)
+ */
+ private long GetMQuote()
+ {
+ Debug.Assert(this.sign > 0);
+
+ if (mQuote != -1)
+ {
+ return mQuote; // already calculated
+ }
+
+ if (magnitude.Length == 0 || (magnitude[magnitude.Length - 1] & 1) == 0)
+ {
+ return -1; // not for even numbers
+ }
+
+ long v = (((~this.magnitude[this.magnitude.Length - 1]) | 1) & 0xffffffffL);
+ mQuote = FastModInverse(v, 0x100000000L);
+
+ return mQuote;
+ }
+
+ /**
+ * Montgomery multiplication: a = x * y * R^(-1) mod m
+ *
+ * Based algorithm 14.36 of Handbook of Applied Cryptography.
+ *
+ *
m, x, y should have length n
+ *
a should have length (n + 1)
+ *
b = 2^32, R = b^n
+ *
+ * The result is put in x
+ *
+ * NOTE: the indices of x, y, m, a different in HAC and in Java
+ */
+ private static void MultiplyMonty(
+ int[] a,
+ int[] x,
+ int[] y,
+ int[] m,
+ long mQuote)
+ // mQuote = -m^(-1) mod b
+ {
+ if (m.Length == 1)
+ {
+ x[0] = (int)MultiplyMontyNIsOne((uint)x[0], (uint)y[0], (uint)m[0], (ulong)mQuote);
+ return;
+ }
+
+ int n = m.Length;
+ int nMinus1 = n - 1;
+ long y_0 = y[nMinus1] & IMASK;
+
+ // 1. a = 0 (Notation: a = (a_{n} a_{n-1} ... a_{0})_{b} )
+ Array.Clear(a, 0, n + 1);
+
+ // 2. for i from 0 to (n - 1) do the following:
+ for (int i = n; i > 0; i--)
+ {
+ long x_i = x[i - 1] & IMASK;
+
+ // 2.1 u = ((a[0] + (x[i] * y[0]) * mQuote) mod b
+ long u = ((((a[n] & IMASK) + ((x_i * y_0) & IMASK)) & IMASK) * mQuote) & IMASK;
+
+ // 2.2 a = (a + x_i * y + u * m) / b
+ long prod1 = x_i * y_0;
+ long prod2 = u * (m[nMinus1] & IMASK);
+ long tmp = (a[n] & IMASK) + (prod1 & IMASK) + (prod2 & IMASK);
+ long carry = (long)((ulong)prod1 >> 32) + (long)((ulong)prod2 >> 32) + (long)((ulong)tmp >> 32);
+ for (int j = nMinus1; j > 0; j--)
+ {
+ prod1 = x_i * (y[j - 1] & IMASK);
+ prod2 = u * (m[j - 1] & IMASK);
+ tmp = (a[j] & IMASK) + (prod1 & IMASK) + (prod2 & IMASK) + (carry & IMASK);
+ carry = (long)((ulong)carry >> 32) + (long)((ulong)prod1 >> 32) +
+ (long)((ulong)prod2 >> 32) + (long)((ulong)tmp >> 32);
+ a[j + 1] = (int)tmp; // division by b
+ }
+ carry += (a[0] & IMASK);
+ a[1] = (int)carry;
+ a[0] = (int)((ulong)carry >> 32); // OJO!!!!!
+ }
+
+ // 3. if x >= m the x = x - m
+ if (CompareTo(0, a, 0, m) >= 0)
+ {
+ Subtract(0, a, 0, m);
+ }
+
+ // put the result in x
+ Array.Copy(a, 1, x, 0, n);
+ }
+
+ private static uint MultiplyMontyNIsOne(
+ uint x,
+ uint y,
+ uint m,
+ ulong mQuote)
+ {
+ ulong um = m;
+ ulong prod1 = (ulong)x * (ulong)y;
+ ulong u = (prod1 * mQuote) & UIMASK;
+ ulong prod2 = u * um;
+ ulong tmp = (prod1 & UIMASK) + (prod2 & UIMASK);
+ ulong carry = (prod1 >> 32) + (prod2 >> 32) + (tmp >> 32);
+
+ if (carry > um)
+ {
+ carry -= um;
+ }
+
+ return (uint)(carry & UIMASK);
+ }
+
+ public BigInteger Multiply(
+ BigInteger val)
+ {
+ if (sign == 0 || val.sign == 0)
+ return Zero;
+
+ if (val.QuickPow2Check()) // val is power of two
+ {
+ BigInteger result = this.ShiftLeft(val.Abs().BitLength - 1);
+ return val.sign > 0 ? result : result.Negate();
+ }
+
+ if (this.QuickPow2Check()) // this is power of two
+ {
+ BigInteger result = val.ShiftLeft(this.Abs().BitLength - 1);
+ return this.sign > 0 ? result : result.Negate();
+ }
+
+ int resLength = (this.BitLength + val.BitLength) / BitsPerInt + 1;
+ int[] res = new int[resLength];
+
+ if (val == this)
+ {
+ Square(res, this.magnitude);
+ }
+ else
+ {
+ Multiply(res, this.magnitude, val.magnitude);
+ }
+
+ return new BigInteger(sign * val.sign, res, true);
+ }
+
+ public BigInteger Negate()
+ {
+ if (sign == 0)
+ return this;
+
+ return new BigInteger(-sign, magnitude, false);
+ }
+
+ public BigInteger NextProbablePrime()
+ {
+ if (sign < 0)
+ throw new ArithmeticException("Cannot be called on value < 0");
+
+ if (CompareTo(Two) < 0)
+ return Two;
+
+ BigInteger n = Inc().SetBit(0);
+
+ while (!n.CheckProbablePrime(100, RandomSource))
+ {
+ n = n.Add(Two);
+ }
+
+ return n;
+ }
+
+ public BigInteger Not()
+ {
+ return Inc().Negate();
+ }
+
+ public BigInteger Pow(int exp)
+ {
+ if (exp < 0)
+ {
+ throw new ArithmeticException("Negative exponent");
+ }
+
+ if (exp == 0)
+ {
+ return One;
+ }
+
+ if (sign == 0 || Equals(One))
+ {
+ return this;
+ }
+
+ BigInteger y = One;
+ BigInteger z = this;
+
+ for (; ; )
+ {
+ if ((exp & 0x1) == 1)
+ {
+ y = y.Multiply(z);
+ }
+ exp >>= 1;
+ if (exp == 0)
+ break;
+ z = z.Multiply(z);
+ }
+
+ return y;
+ }
+
+ public static BigInteger ProbablePrime(
+ int bitLength,
+ Random random)
+ {
+ return new BigInteger(bitLength, 100, random);
+ }
+
+ private int Remainder(
+ int m)
+ {
+ Debug.Assert(m > 0);
+
+ long acc = 0;
+ for (int pos = 0; pos < magnitude.Length; ++pos)
+ {
+ long posVal = (uint)magnitude[pos];
+ acc = (acc << 32 | posVal) % m;
+ }
+
+ return (int)acc;
+ }
+
+ /**
+ * return x = x % y - done in place (y value preserved)
+ */
+ private int[] Remainder(
+ int[] x,
+ int[] y)
+ {
+ int xStart = 0;
+ while (xStart < x.Length && x[xStart] == 0)
+ {
+ ++xStart;
+ }
+
+ int yStart = 0;
+ while (yStart < y.Length && y[yStart] == 0)
+ {
+ ++yStart;
+ }
+
+ Debug.Assert(yStart < y.Length);
+
+ int xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+
+ if (xyCmp > 0)
+ {
+ int yBitLength = calcBitLength(yStart, y);
+ int xBitLength = calcBitLength(xStart, x);
+ int shift = xBitLength - yBitLength;
+
+ int[] c;
+ int cStart = 0;
+ int cBitLength = yBitLength;
+ if (shift > 0)
+ {
+ c = ShiftLeft(y, shift);
+ cBitLength += shift;
+ Debug.Assert(c[0] != 0);
+ }
+ else
+ {
+ int len = y.Length - yStart;
+ c = new int[len];
+ Array.Copy(y, yStart, c, 0, len);
+ }
+
+ for (; ; )
+ {
+ if (cBitLength < xBitLength
+ || CompareNoLeadingZeroes(xStart, x, cStart, c) >= 0)
+ {
+ Subtract(xStart, x, cStart, c);
+
+ while (x[xStart] == 0)
+ {
+ if (++xStart == x.Length)
+ return x;
+ }
+
+ //xBitLength = calcBitLength(xStart, x);
+ xBitLength = 32 * (x.Length - xStart - 1) + BitLen(x[xStart]);
+
+ if (xBitLength <= yBitLength)
+ {
+ if (xBitLength < yBitLength)
+ return x;
+
+ xyCmp = CompareNoLeadingZeroes(xStart, x, yStart, y);
+
+ if (xyCmp <= 0)
+ break;
+ }
+ }
+
+ shift = cBitLength - xBitLength;
+
+ // NB: The case where c[cStart] is 1-bit is harmless
+ if (shift == 1)
+ {
+ uint firstC = (uint)c[cStart] >> 1;
+ uint firstX = (uint)x[xStart];
+ if (firstC > firstX)
+ ++shift;
+ }
+
+ if (shift < 2)
+ {
+ ShiftRightOneInPlace(cStart, c);
+ --cBitLength;
+ }
+ else
+ {
+ ShiftRightInPlace(cStart, c, shift);
+ cBitLength -= shift;
+ }
+
+ //cStart = c.Length - ((cBitLength + 31) / 32);
+ while (c[cStart] == 0)
+ {
+ ++cStart;
+ }
+ }
+ }
+
+ if (xyCmp == 0)
+ {
+ Array.Clear(x, xStart, x.Length - xStart);
+ }
+
+ return x;
+ }
+
+ public BigInteger Remainder(
+ BigInteger n)
+ {
+ if (n.sign == 0)
+ throw new ArithmeticException("Division by zero error");
+
+ if (this.sign == 0)
+ return Zero;
+
+ // For small values, use fast remainder method
+ if (n.magnitude.Length == 1)
+ {
+ int val = n.magnitude[0];
+
+ if (val > 0)
+ {
+ if (val == 1)
+ return Zero;
+
+ // TODO Make this func work on uint, and handle val == 1?
+ int rem = Remainder(val);
+
+ return rem == 0
+ ? Zero
+ : new BigInteger(sign, new int[] { rem }, false);
+ }
+ }
+
+ if (CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude) < 0)
+ return this;
+
+ int[] result;
+ if (n.QuickPow2Check()) // n is power of two
+ {
+ // TODO Move before small values branch above?
+ result = LastNBits(n.Abs().BitLength - 1);
+ }
+ else
+ {
+ result = (int[])this.magnitude.Clone();
+ result = Remainder(result, n.magnitude);
+ }
+
+ return new BigInteger(sign, result, true);
+ }
+
+ private int[] LastNBits(
+ int n)
+ {
+ if (n < 1)
+ return ZeroMagnitude;
+
+ int numWords = (n + BitsPerInt - 1) / BitsPerInt;
+ numWords = System.Math.Min(numWords, this.magnitude.Length);
+ int[] result = new int[numWords];
+
+ Array.Copy(this.magnitude, this.magnitude.Length - numWords, result, 0, numWords);
+
+ int hiBits = n % 32;
+ if (hiBits != 0)
+ {
+ result[0] &= ~(-1 << hiBits);
+ }
+
+ return result;
+ }
+
+ /**
+ * do a left shift - this returns a new array.
+ */
+ private static int[] ShiftLeft(
+ int[] mag,
+ int n)
+ {
+ int nInts = (int)((uint)n >> 5);
+ int nBits = n & 0x1f;
+ int magLen = mag.Length;
+ int[] newMag;
+
+ if (nBits == 0)
+ {
+ newMag = new int[magLen + nInts];
+ mag.CopyTo(newMag, 0);
+ }
+ else
+ {
+ int i = 0;
+ int nBits2 = 32 - nBits;
+ int highBits = (int)((uint)mag[0] >> nBits2);
+
+ if (highBits != 0)
+ {
+ newMag = new int[magLen + nInts + 1];
+ newMag[i++] = highBits;
+ }
+ else
+ {
+ newMag = new int[magLen + nInts];
+ }
+
+ int m = mag[0];
+ for (int j = 0; j < magLen - 1; j++)
+ {
+ int next = mag[j + 1];
+
+ newMag[i++] = (m << nBits) | (int)((uint)next >> nBits2);
+ m = next;
+ }
+
+ newMag[i] = mag[magLen - 1] << nBits;
+ }
+
+ return newMag;
+ }
+
+ public BigInteger ShiftLeft(
+ int n)
+ {
+ if (sign == 0 || magnitude.Length == 0)
+ return Zero;
+
+ if (n == 0)
+ return this;
+
+ if (n < 0)
+ return ShiftRight(-n);
+
+ BigInteger result = new BigInteger(sign, ShiftLeft(magnitude, n), true);
+
+ if (this.nBits != -1)
+ {
+ result.nBits = sign > 0
+ ? this.nBits
+ : this.nBits + n;
+ }
+
+ if (this.nBitLength != -1)
+ {
+ result.nBitLength = this.nBitLength + n;
+ }
+
+ return result;
+ }
+
+ /**
+ * do a right shift - this does it in place.
+ */
+ private static void ShiftRightInPlace(
+ int start,
+ int[] mag,
+ int n)
+ {
+ int nInts = (int)((uint)n >> 5) + start;
+ int nBits = n & 0x1f;
+ int magEnd = mag.Length - 1;
+
+ if (nInts != start)
+ {
+ int delta = (nInts - start);
+
+ for (int i = magEnd; i >= nInts; i--)
+ {
+ mag[i] = mag[i - delta];
+ }
+ for (int i = nInts - 1; i >= start; i--)
+ {
+ mag[i] = 0;
+ }
+ }
+
+ if (nBits != 0)
+ {
+ int nBits2 = 32 - nBits;
+ int m = mag[magEnd];
+
+ for (int i = magEnd; i > nInts; --i)
+ {
+ int next = mag[i - 1];
+
+ mag[i] = (int)((uint)m >> nBits) | (next << nBits2);
+ m = next;
+ }
+
+ mag[nInts] = (int)((uint)mag[nInts] >> nBits);
+ }
+ }
+
+ /**
+ * do a right shift by one - this does it in place.
+ */
+ private static void ShiftRightOneInPlace(
+ int start,
+ int[] mag)
+ {
+ int i = mag.Length;
+ int m = mag[i - 1];
+
+ while (--i > start)
+ {
+ int next = mag[i - 1];
+ mag[i] = ((int)((uint)m >> 1)) | (next << 31);
+ m = next;
+ }
+
+ mag[start] = (int)((uint)mag[start] >> 1);
+ }
+
+ public BigInteger ShiftRight(
+ int n)
+ {
+ if (n == 0)
+ return this;
+
+ if (n < 0)
+ return ShiftLeft(-n);
+
+ if (n >= BitLength)
+ return (this.sign < 0 ? One.Negate() : Zero);
+
+ // int[] res = (int[]) this.magnitude.Clone();
+ //
+ // ShiftRightInPlace(0, res, n);
+ //
+ // return new BigInteger(this.sign, res, true);
+
+ int resultLength = (BitLength - n + 31) >> 5;
+ int[] res = new int[resultLength];
+
+ int numInts = n >> 5;
+ int numBits = n & 31;
+
+ if (numBits == 0)
+ {
+ Array.Copy(this.magnitude, 0, res, 0, res.Length);
+ }
+ else
+ {
+ int numBits2 = 32 - numBits;
+
+ int magPos = this.magnitude.Length - 1 - numInts;
+ for (int i = resultLength - 1; i >= 0; --i)
+ {
+ res[i] = (int)((uint)this.magnitude[magPos--] >> numBits);
+
+ if (magPos >= 0)
+ {
+ res[i] |= this.magnitude[magPos] << numBits2;
+ }
+ }
+ }
+
+ Debug.Assert(res[0] != 0);
+
+ return new BigInteger(this.sign, res, false);
+ }
+
+ public int SignValue
+ {
+ get { return sign; }
+ }
+
+ /**
+ * returns x = x - y - we assume x is >= y
+ */
+ private static int[] Subtract(
+ int xStart,
+ int[] x,
+ int yStart,
+ int[] y)
+ {
+ Debug.Assert(yStart < y.Length);
+ Debug.Assert(x.Length - xStart >= y.Length - yStart);
+
+ int iT = x.Length;
+ int iV = y.Length;
+ long m;
+ int borrow = 0;
+
+ do
+ {
+ m = (x[--iT] & IMASK) - (y[--iV] & IMASK) + borrow;
+ x[iT] = (int)m;
+
+ // borrow = (m < 0) ? -1 : 0;
+ borrow = (int)(m >> 63);
+ }
+ while (iV > yStart);
+
+ if (borrow != 0)
+ {
+ while (--x[--iT] == -1)
+ {
+ }
+ }
+
+ return x;
+ }
+
+ public BigInteger Subtract(
+ BigInteger n)
+ {
+ if (n.sign == 0)
+ return this;
+
+ if (this.sign == 0)
+ return n.Negate();
+
+ if (this.sign != n.sign)
+ return Add(n.Negate());
+
+ int compare = CompareNoLeadingZeroes(0, magnitude, 0, n.magnitude);
+ if (compare == 0)
+ return Zero;
+
+ BigInteger bigun, lilun;
+ if (compare < 0)
+ {
+ bigun = n;
+ lilun = this;
+ }
+ else
+ {
+ bigun = this;
+ lilun = n;
+ }
+
+ return new BigInteger(this.sign * compare, doSubBigLil(bigun.magnitude, lilun.magnitude), true);
+ }
+
+ private static int[] doSubBigLil(
+ int[] bigMag,
+ int[] lilMag)
+ {
+ int[] res = (int[])bigMag.Clone();
+
+ return Subtract(0, res, 0, lilMag);
+ }
+
+ public byte[] ToByteArray()
+ {
+ return ToByteArray(false);
+ }
+
+ public byte[] ToByteArrayUnsigned()
+ {
+ return ToByteArray(true);
+ }
+
+ private byte[] ToByteArray(
+ bool unsigned)
+ {
+ if (sign == 0)
+ return unsigned ? ZeroEncoding : new byte[1];
+
+ int nBits = (unsigned && sign > 0)
+ ? BitLength
+ : BitLength + 1;
+
+ int nBytes = GetByteLength(nBits);
+ byte[] bytes = new byte[nBytes];
+
+ int magIndex = magnitude.Length;
+ int bytesIndex = bytes.Length;
+
+ if (sign > 0)
+ {
+ while (magIndex > 1)
+ {
+ uint mag = (uint)magnitude[--magIndex];
+ bytes[--bytesIndex] = (byte)mag;
+ bytes[--bytesIndex] = (byte)(mag >> 8);
+ bytes[--bytesIndex] = (byte)(mag >> 16);
+ bytes[--bytesIndex] = (byte)(mag >> 24);
+ }
+
+ uint lastMag = (uint)magnitude[0];
+ while (lastMag > byte.MaxValue)
+ {
+ bytes[--bytesIndex] = (byte)lastMag;
+ lastMag >>= 8;
+ }
+
+ bytes[--bytesIndex] = (byte)lastMag;
+ }
+ else // sign < 0
+ {
+ bool carry = true;
+
+ while (magIndex > 1)
+ {
+ uint mag = ~((uint)magnitude[--magIndex]);
+
+ if (carry)
+ {
+ carry = (++mag == uint.MinValue);
+ }
+
+ bytes[--bytesIndex] = (byte)mag;
+ bytes[--bytesIndex] = (byte)(mag >> 8);
+ bytes[--bytesIndex] = (byte)(mag >> 16);
+ bytes[--bytesIndex] = (byte)(mag >> 24);
+ }
+
+ uint lastMag = (uint)magnitude[0];
+
+ if (carry)
+ {
+ // Never wraps because magnitude[0] != 0
+ --lastMag;
+ }
+
+ while (lastMag > byte.MaxValue)
+ {
+ bytes[--bytesIndex] = (byte)~lastMag;
+ lastMag >>= 8;
+ }
+
+ bytes[--bytesIndex] = (byte)~lastMag;
+
+ if (bytesIndex > 0)
+ {
+ bytes[--bytesIndex] = byte.MaxValue;
+ }
+ }
+
+ return bytes;
+ }
+
+ public override string ToString()
+ {
+ return ToString(10);
+ }
+
+ public string ToString(
+ int radix)
+ {
+ // TODO Make this method work for other radices (ideally 2 <= radix <= 16)
+
+ switch (radix)
+ {
+ case 2:
+ case 10:
+ case 16:
+ break;
+ default:
+ throw new FormatException("Only bases 2, 10, 16 are allowed");
+ }
+
+ // NB: Can only happen to internally managed instances
+ if (magnitude == null)
+ return "null";
+
+ if (sign == 0)
+ return "0";
+
+ Debug.Assert(magnitude.Length > 0);
+
+ StringBuilder sb = new StringBuilder();
+
+ if (radix == 16)
+ {
+ sb.Append(magnitude[0].ToString("x"));
+
+ for (int i = 1; i < magnitude.Length; i++)
+ {
+ sb.Append(magnitude[i].ToString("x8"));
+ }
+ }
+ else if (radix == 2)
+ {
+ sb.Append('1');
+
+ for (int i = BitLength - 2; i >= 0; --i)
+ {
+ sb.Append(TestBit(i) ? '1' : '0');
+ }
+ }
+ else
+ {
+ // This is algorithm 1a from chapter 4.4 in Seminumerical Algorithms, slow but it works
+ var S = new List();
+ BigInteger bs = ValueOf(radix);
+
+ // The sign is handled separatly.
+ // Notice however that for this to work, radix 16 _MUST_ be a special case,
+ // unless we want to enter a recursion well. In their infinite wisdom, why did not
+ // the Sun engineers made a c'tor for BigIntegers taking a BigInteger as parameter?
+ // (Answer: Becuase Sun's BigIntger is clonable, something bouncycastle's isn't.)
+ // BigInteger u = new BigInteger(Abs().ToString(16), 16);
+ BigInteger u = this.Abs();
+ BigInteger b;
+
+ while (u.sign != 0)
+ {
+ b = u.Mod(bs);
+ if (b.sign == 0)
+ {
+ S.Add("0");
+ }
+ else
+ {
+ // see how to interact with different bases
+ S.Add(b.magnitude[0].ToString("d"));
+ }
+ u = u.Divide(bs);
+ }
+
+ // Then pop the stack
+ for (int i = S.Count - 1; i >= 0; --i)
+ {
+ sb.Append((string)S[i]);
+ }
+ }
+
+ string s = sb.ToString();
+
+ Debug.Assert(s.Length > 0);
+
+ // Strip leading zeros. (We know this number is not all zeroes though)
+ if (s[0] == '0')
+ {
+ int nonZeroPos = 0;
+ while (s[++nonZeroPos] == '0') { }
+
+ s = s.Substring(nonZeroPos);
+ }
+
+ if (sign == -1)
+ {
+ s = "-" + s;
+ }
+
+ return s;
+ }
+
+ private static BigInteger createUValueOf(
+ ulong value)
+ {
+ int msw = (int)(value >> 32);
+ int lsw = (int)value;
+
+ if (msw != 0)
+ return new BigInteger(1, new int[] { msw, lsw }, false);
+
+ if (lsw != 0)
+ {
+ BigInteger n = new BigInteger(1, new int[] { lsw }, false);
+ // Check for a power of two
+ if ((lsw & -lsw) == lsw)
+ {
+ n.nBits = 1;
+ }
+ return n;
+ }
+
+ return Zero;
+ }
+
+ private static BigInteger createValueOf(
+ long value)
+ {
+ if (value < 0)
+ {
+ if (value == long.MinValue)
+ return createValueOf(~value).Not();
+
+ return createValueOf(-value).Negate();
+ }
+
+ return createUValueOf((ulong)value);
+
+ // // store value into a byte array
+ // byte[] b = new byte[8];
+ // for (int i = 0; i < 8; i++)
+ // {
+ // b[7 - i] = (byte)value;
+ // value >>= 8;
+ // }
+ //
+ // return new BigInteger(b);
+ }
+
+ public static BigInteger ValueOf(
+ long value)
+ {
+ switch (value)
+ {
+ case 0:
+ return Zero;
+ case 1:
+ return One;
+ case 2:
+ return Two;
+ case 3:
+ return Three;
+ case 10:
+ return Ten;
+ }
+
+ return createValueOf(value);
+ }
+
+ public int GetLowestSetBit()
+ {
+ if (this.sign == 0)
+ return -1;
+
+ int w = magnitude.Length;
+
+ while (--w > 0)
+ {
+ if (magnitude[w] != 0)
+ break;
+ }
+
+ int word = (int)magnitude[w];
+ Debug.Assert(word != 0);
+
+ int b = (word & 0x0000FFFF) == 0
+ ? (word & 0x00FF0000) == 0
+ ? 7
+ : 15
+ : (word & 0x000000FF) == 0
+ ? 23
+ : 31;
+
+ while (b > 0)
+ {
+ if ((word << b) == int.MinValue)
+ break;
+
+ b--;
+ }
+
+ return ((magnitude.Length - w) * 32 - (b + 1));
+ }
+
+ public bool TestBit(
+ int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Bit position must not be negative");
+
+ if (sign < 0)
+ return !Not().TestBit(n);
+
+ int wordNum = n / 32;
+ if (wordNum >= magnitude.Length)
+ return false;
+
+ int word = magnitude[magnitude.Length - 1 - wordNum];
+ return ((word >> (n % 32)) & 1) > 0;
+ }
+
+ public BigInteger Or(
+ BigInteger value)
+ {
+ if (this.sign == 0)
+ return value;
+
+ if (value.sign == 0)
+ return this;
+
+ int[] aMag = this.sign > 0
+ ? this.magnitude
+ : Add(One).magnitude;
+
+ int[] bMag = value.sign > 0
+ ? value.magnitude
+ : value.Add(One).magnitude;
+
+ bool resultNeg = sign < 0 || value.sign < 0;
+ int resultLength = System.Math.Max(aMag.Length, bMag.Length);
+ int[] resultMag = new int[resultLength];
+
+ int aStart = resultMag.Length - aMag.Length;
+ int bStart = resultMag.Length - bMag.Length;
+
+ for (int i = 0; i < resultMag.Length; ++i)
+ {
+ int aWord = i >= aStart ? aMag[i - aStart] : 0;
+ int bWord = i >= bStart ? bMag[i - bStart] : 0;
+
+ if (this.sign < 0)
+ {
+ aWord = ~aWord;
+ }
+
+ if (value.sign < 0)
+ {
+ bWord = ~bWord;
+ }
+
+ resultMag[i] = aWord | bWord;
+
+ if (resultNeg)
+ {
+ resultMag[i] = ~resultMag[i];
+ }
+ }
+
+ BigInteger result = new BigInteger(1, resultMag, true);
+
+ // TODO Optimise this case
+ if (resultNeg)
+ {
+ result = result.Not();
+ }
+
+ return result;
+ }
+
+ public BigInteger Xor(
+ BigInteger value)
+ {
+ if (this.sign == 0)
+ return value;
+
+ if (value.sign == 0)
+ return this;
+
+ int[] aMag = this.sign > 0
+ ? this.magnitude
+ : Add(One).magnitude;
+
+ int[] bMag = value.sign > 0
+ ? value.magnitude
+ : value.Add(One).magnitude;
+
+ // TODO Can just replace with sign != value.sign?
+ bool resultNeg = (sign < 0 && value.sign >= 0) || (sign >= 0 && value.sign < 0);
+ int resultLength = System.Math.Max(aMag.Length, bMag.Length);
+ int[] resultMag = new int[resultLength];
+
+ int aStart = resultMag.Length - aMag.Length;
+ int bStart = resultMag.Length - bMag.Length;
+
+ for (int i = 0; i < resultMag.Length; ++i)
+ {
+ int aWord = i >= aStart ? aMag[i - aStart] : 0;
+ int bWord = i >= bStart ? bMag[i - bStart] : 0;
+
+ if (this.sign < 0)
+ {
+ aWord = ~aWord;
+ }
+
+ if (value.sign < 0)
+ {
+ bWord = ~bWord;
+ }
+
+ resultMag[i] = aWord ^ bWord;
+
+ if (resultNeg)
+ {
+ resultMag[i] = ~resultMag[i];
+ }
+ }
+
+ BigInteger result = new BigInteger(1, resultMag, true);
+
+ // TODO Optimise this case
+ if (resultNeg)
+ {
+ result = result.Not();
+ }
+
+ return result;
+ }
+
+ public BigInteger SetBit(
+ int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Bit address less than zero");
+
+ if (TestBit(n))
+ return this;
+
+ // TODO Handle negative values and zero
+ if (sign > 0 && n < (BitLength - 1))
+ return FlipExistingBit(n);
+
+ return Or(One.ShiftLeft(n));
+ }
+
+ public BigInteger ClearBit(
+ int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Bit address less than zero");
+
+ if (!TestBit(n))
+ return this;
+
+ // TODO Handle negative values
+ if (sign > 0 && n < (BitLength - 1))
+ return FlipExistingBit(n);
+
+ return AndNot(One.ShiftLeft(n));
+ }
+
+ public BigInteger FlipBit(
+ int n)
+ {
+ if (n < 0)
+ throw new ArithmeticException("Bit address less than zero");
+
+ // TODO Handle negative values and zero
+ if (sign > 0 && n < (BitLength - 1))
+ return FlipExistingBit(n);
+
+ return Xor(One.ShiftLeft(n));
+ }
+
+ private BigInteger FlipExistingBit(
+ int n)
+ {
+ Debug.Assert(sign > 0);
+ Debug.Assert(n >= 0);
+ Debug.Assert(n < BitLength - 1);
+
+ int[] mag = (int[])this.magnitude.Clone();
+ mag[mag.Length - 1 - (n >> 5)] ^= (1 << (n & 31)); // Flip bit
+ //mag[mag.Length - 1 - (n / 32)] ^= (1 << (n % 32));
+ return new BigInteger(this.sign, mag, false);
+ }
+ }
+}
diff --git a/SharpCompress/Crypto/BufferedBlockCipher.cs b/SharpCompress/Crypto/BufferedBlockCipher.cs
new file mode 100644
index 00000000..c7c06b3b
--- /dev/null
+++ b/SharpCompress/Crypto/BufferedBlockCipher.cs
@@ -0,0 +1,376 @@
+using System;
+
+
+namespace Org.BouncyCastle.Crypto
+{
+ /**
+ * A wrapper class that allows block ciphers to be used to process data in
+ * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
+ * buffer is full and more data is being added, or on a doFinal.
+ *
+ * Note: in the case where the underlying cipher is either a CFB cipher or an
+ * OFB one the last block may not be a multiple of the block size.
+ *
+ */
+ public class BufferedBlockCipher
+ : BufferedCipherBase
+ {
+ internal byte[] buf;
+ internal int bufOff;
+ internal bool forEncryption;
+ internal IBlockCipher cipher;
+
+ /**
+ * constructor for subclasses
+ */
+ protected BufferedBlockCipher()
+ {
+ }
+
+ /**
+ * Create a buffered block cipher without padding.
+ *
+ * @param cipher the underlying block cipher this buffering object wraps.
+ * false otherwise.
+ */
+ public BufferedBlockCipher(
+ IBlockCipher cipher)
+ {
+ if (cipher == null)
+ throw new ArgumentNullException("cipher");
+
+ this.cipher = cipher;
+ buf = new byte[cipher.GetBlockSize()];
+ bufOff = 0;
+ }
+
+ public override string AlgorithmName
+ {
+ get { return cipher.AlgorithmName; }
+ }
+
+ /**
+ * initialise the cipher.
+ *
+ * @param forEncryption if true the cipher is initialised for
+ * encryption, if false for decryption.
+ * @param param the key and other data required by the cipher.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ // Note: This doubles as the Init in the event that this cipher is being used as an IWrapper
+ public override void Init(
+ bool forEncryption,
+ ICipherParameters parameters)
+ {
+ this.forEncryption = forEncryption;
+
+ //if (parameters is ParametersWithRandom)
+ //{
+ // parameters = ((ParametersWithRandom) parameters).Parameters;
+ //}
+
+ Reset();
+
+ cipher.Init(forEncryption, parameters);
+ }
+
+ /**
+ * return the blocksize for the underlying cipher.
+ *
+ * @return the blocksize for the underlying cipher.
+ */
+ public override int GetBlockSize()
+ {
+ return cipher.GetBlockSize();
+ }
+
+ /**
+ * return the size of the output buffer required for an update
+ * an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update
+ * with len bytes of input.
+ */
+ public override int GetUpdateOutputSize(
+ int length)
+ {
+ int total = length + bufOff;
+ int leftOver = total % buf.Length;
+ return total - leftOver;
+ }
+
+ /**
+ * return the size of the output buffer required for an update plus a
+ * doFinal with an input of len bytes.
+ *
+ * @param len the length of the input.
+ * @return the space required to accommodate a call to update and doFinal
+ * with len bytes of input.
+ */
+ public override int GetOutputSize(
+ int length)
+ {
+ // Note: Can assume IsPartialBlockOkay is true for purposes of this calculation
+ return length + bufOff;
+ }
+
+ /**
+ * process a single byte, producing an output block if neccessary.
+ *
+ * @param in the input byte.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ buf[bufOff++] = input;
+
+ if (bufOff == buf.Length)
+ {
+ if ((outOff + buf.Length) > output.Length)
+ throw new DataLengthException("output buffer too short");
+
+ bufOff = 0;
+ return cipher.ProcessBlock(buf, 0, output, outOff);
+ }
+
+ return 0;
+ }
+
+ public override byte[] ProcessByte(
+ byte input)
+ {
+ int outLength = GetUpdateOutputSize(1);
+
+ byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
+
+ int pos = ProcessByte(input, outBytes, 0);
+
+ if (outLength > 0 && pos < outLength)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+
+ return outBytes;
+ }
+
+ public override byte[] ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+ if (length < 1)
+ return null;
+
+ int outLength = GetUpdateOutputSize(length);
+
+ byte[] outBytes = outLength > 0 ? new byte[outLength] : null;
+
+ int pos = ProcessBytes(input, inOff, length, outBytes, 0);
+
+ if (outLength > 0 && pos < outLength)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * process an array of bytes, producing output if necessary.
+ *
+ * @param in the input byte array.
+ * @param inOff the offset at which the input data starts.
+ * @param len the number of bytes to be copied out of the input array.
+ * @param out the space for any output that might be produced.
+ * @param outOff the offset from which the output will be copied.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there isn't enough space in out.
+ * @exception InvalidOperationException if the cipher isn't initialised.
+ */
+ public override int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ if (length < 1)
+ {
+ if (length < 0)
+ throw new ArgumentException("Can't have a negative input length!");
+
+ return 0;
+ }
+
+ int blockSize = GetBlockSize();
+ int outLength = GetUpdateOutputSize(length);
+
+ if (outLength > 0)
+ {
+ if ((outOff + outLength) > output.Length)
+ {
+ throw new DataLengthException("output buffer too short");
+ }
+ }
+
+ int resultLen = 0;
+ int gapLen = buf.Length - bufOff;
+ if (length > gapLen)
+ {
+ Array.Copy(input, inOff, buf, bufOff, gapLen);
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff);
+ bufOff = 0;
+ length -= gapLen;
+ inOff += gapLen;
+ while (length > buf.Length)
+ {
+ resultLen += cipher.ProcessBlock(input, inOff, output, outOff + resultLen);
+ length -= blockSize;
+ inOff += blockSize;
+ }
+ }
+ Array.Copy(input, inOff, buf, bufOff, length);
+ bufOff += length;
+ if (bufOff == buf.Length)
+ {
+ resultLen += cipher.ProcessBlock(buf, 0, output, outOff + resultLen);
+ bufOff = 0;
+ }
+ return resultLen;
+ }
+
+ public override byte[] DoFinal()
+ {
+ byte[] outBytes = EmptyBuffer;
+
+ int length = GetOutputSize(0);
+ if (length > 0)
+ {
+ outBytes = new byte[length];
+
+ int pos = DoFinal(outBytes, 0);
+ if (pos < outBytes.Length)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+ }
+ else
+ {
+ Reset();
+ }
+
+ return outBytes;
+ }
+
+ public override byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int inLen)
+ {
+ if (input == null)
+ throw new ArgumentNullException("input");
+
+ int length = GetOutputSize(inLen);
+
+ byte[] outBytes = EmptyBuffer;
+
+ if (length > 0)
+ {
+ outBytes = new byte[length];
+
+ int pos = (inLen > 0)
+ ? ProcessBytes(input, inOff, inLen, outBytes, 0)
+ : 0;
+
+ pos += DoFinal(outBytes, pos);
+
+ if (pos < outBytes.Length)
+ {
+ byte[] tmp = new byte[pos];
+ Array.Copy(outBytes, 0, tmp, 0, pos);
+ outBytes = tmp;
+ }
+ }
+ else
+ {
+ Reset();
+ }
+
+ return outBytes;
+ }
+
+ /**
+ * Process the last block in the buffer.
+ *
+ * @param out the array the block currently being held is copied into.
+ * @param outOff the offset at which the copying starts.
+ * @return the number of output bytes copied to out.
+ * @exception DataLengthException if there is insufficient space in out for
+ * the output, or the input is not block size aligned and should be.
+ * @exception InvalidOperationException if the underlying cipher is not
+ * initialised.
+ * @exception InvalidCipherTextException if padding is expected and not found.
+ * @exception DataLengthException if the input is not block size
+ * aligned.
+ */
+ public override int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ try
+ {
+ if (bufOff != 0)
+ {
+ if (!cipher.IsPartialBlockOkay)
+ {
+ throw new DataLengthException("data not block size aligned");
+ }
+
+ if (outOff + bufOff > output.Length)
+ {
+ throw new DataLengthException("output buffer too short for DoFinal()");
+ }
+
+ // NB: Can't copy directly, or we may write too much output
+ cipher.ProcessBlock(buf, 0, buf, 0);
+ Array.Copy(buf, 0, output, outOff, bufOff);
+ }
+
+ return bufOff;
+ }
+ finally
+ {
+ Reset();
+ }
+ }
+
+ /**
+ * Reset the buffer and cipher. After resetting the object is in the same
+ * state as it was after the last init (if there was one).
+ */
+ public override void Reset()
+ {
+ Array.Clear(buf, 0, buf.Length);
+ bufOff = 0;
+
+ cipher.Reset();
+ }
+ }
+}
diff --git a/SharpCompress/Crypto/BufferedCipherBase.cs b/SharpCompress/Crypto/BufferedCipherBase.cs
new file mode 100644
index 00000000..f87f38c2
--- /dev/null
+++ b/SharpCompress/Crypto/BufferedCipherBase.cs
@@ -0,0 +1,113 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ public abstract class BufferedCipherBase
+ : IBufferedCipher
+ {
+ protected static readonly byte[] EmptyBuffer = new byte[0];
+
+ public abstract string AlgorithmName { get; }
+
+ public abstract void Init(bool forEncryption, ICipherParameters parameters);
+
+ public abstract int GetBlockSize();
+
+ public abstract int GetOutputSize(int inputLen);
+ public abstract int GetUpdateOutputSize(int inputLen);
+
+ public abstract byte[] ProcessByte(byte input);
+
+ public virtual int ProcessByte(
+ byte input,
+ byte[] output,
+ int outOff)
+ {
+ byte[] outBytes = ProcessByte(input);
+ if (outBytes == null)
+ return 0;
+ if (outOff + outBytes.Length > output.Length)
+ throw new DataLengthException("output buffer too short");
+ outBytes.CopyTo(output, outOff);
+ return outBytes.Length;
+ }
+
+ public virtual byte[] ProcessBytes(
+ byte[] input)
+ {
+ return ProcessBytes(input, 0, input.Length);
+ }
+
+ public abstract byte[] ProcessBytes(byte[] input, int inOff, int length);
+
+ public virtual int ProcessBytes(
+ byte[] input,
+ byte[] output,
+ int outOff)
+ {
+ return ProcessBytes(input, 0, input.Length, output, outOff);
+ }
+
+ public virtual int ProcessBytes(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ byte[] outBytes = ProcessBytes(input, inOff, length);
+ if (outBytes == null)
+ return 0;
+ if (outOff + outBytes.Length > output.Length)
+ throw new DataLengthException("output buffer too short");
+ outBytes.CopyTo(output, outOff);
+ return outBytes.Length;
+ }
+
+ public abstract byte[] DoFinal();
+
+ public virtual byte[] DoFinal(
+ byte[] input)
+ {
+ return DoFinal(input, 0, input.Length);
+ }
+
+ public abstract byte[] DoFinal(
+ byte[] input,
+ int inOff,
+ int length);
+
+ public virtual int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ byte[] outBytes = DoFinal();
+ if (outOff + outBytes.Length > output.Length)
+ throw new DataLengthException("output buffer too short");
+ outBytes.CopyTo(output, outOff);
+ return outBytes.Length;
+ }
+
+ public virtual int DoFinal(
+ byte[] input,
+ byte[] output,
+ int outOff)
+ {
+ return DoFinal(input, 0, input.Length, output, outOff);
+ }
+
+ public virtual int DoFinal(
+ byte[] input,
+ int inOff,
+ int length,
+ byte[] output,
+ int outOff)
+ {
+ int len = ProcessBytes(input, inOff, length, output, outOff);
+ len += DoFinal(output, outOff + len);
+ return len;
+ }
+
+ public abstract void Reset();
+ }
+}
diff --git a/SharpCompress/Crypto/HMac.cs b/SharpCompress/Crypto/HMac.cs
new file mode 100644
index 00000000..ecbd72f3
--- /dev/null
+++ b/SharpCompress/Crypto/HMac.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /**
+ * HMAC implementation based on RFC2104
+ *
+ * H(K XOR opad, H(K XOR ipad, text))
+ */
+ public class HMac
+ : IMac
+ {
+ private const byte IPAD = (byte)0x36;
+ private const byte OPAD = (byte)0x5C;
+
+ private readonly IDigest digest;
+ private readonly int digestSize;
+ private readonly int blockLength;
+
+ private readonly byte[] inputPad;
+ private readonly byte[] outputPad;
+
+ public HMac(
+ IDigest digest)
+ {
+ this.digest = digest;
+ this.digestSize = digest.GetDigestSize();
+ this.blockLength = digest.GetByteLength();
+ this.inputPad = new byte[blockLength];
+ this.outputPad = new byte[blockLength];
+ }
+
+ public string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "/HMAC"; }
+ }
+
+ public IDigest GetUnderlyingDigest()
+ {
+ return digest;
+ }
+
+ public void Init(
+ ICipherParameters parameters)
+ {
+ digest.Reset();
+
+ byte[] key = ((KeyParameter)parameters).GetKey();
+ int keyLength = key.Length;
+
+ if (keyLength > blockLength)
+ {
+ digest.BlockUpdate(key, 0, key.Length);
+ digest.DoFinal(inputPad, 0);
+
+ keyLength = digestSize;
+ }
+ else
+ {
+ Array.Copy(key, 0, inputPad, 0, keyLength);
+ }
+
+ Array.Clear(inputPad, keyLength, blockLength - keyLength);
+ Array.Copy(inputPad, 0, outputPad, 0, blockLength);
+
+ xor(inputPad, IPAD);
+ xor(outputPad, OPAD);
+
+ // Initialise the digest
+ digest.BlockUpdate(inputPad, 0, inputPad.Length);
+ }
+
+ public int GetMacSize()
+ {
+ return digestSize;
+ }
+
+ public void Update(
+ byte input)
+ {
+ digest.Update(input);
+ }
+
+ public void BlockUpdate(
+ byte[] input,
+ int inOff,
+ int len)
+ {
+ digest.BlockUpdate(input, inOff, len);
+ }
+
+ public int DoFinal(
+ byte[] output,
+ int outOff)
+ {
+ byte[] tmp = new byte[digestSize];
+ digest.DoFinal(tmp, 0);
+
+ digest.BlockUpdate(outputPad, 0, outputPad.Length);
+ digest.BlockUpdate(tmp, 0, tmp.Length);
+
+ int len = digest.DoFinal(output, outOff);
+
+ // Initialise the digest
+ digest.BlockUpdate(inputPad, 0, inputPad.Length);
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public void Reset()
+ {
+ // Reset underlying digest
+ digest.Reset();
+
+ // Initialise the digest
+ digest.BlockUpdate(inputPad, 0, inputPad.Length);
+ }
+
+ private static void xor(byte[] a, byte n)
+ {
+ for (int i = 0; i < a.Length; ++i)
+ {
+ a[i] ^= n;
+ }
+ }
+ }
+}
diff --git a/SharpCompress/Crypto/IBlockCipher.cs b/SharpCompress/Crypto/IBlockCipher.cs
index aed81b23..cdb1d493 100644
--- a/SharpCompress/Crypto/IBlockCipher.cs
+++ b/SharpCompress/Crypto/IBlockCipher.cs
@@ -1,5 +1,4 @@
-using Org.BouncyCastle.Crypto.Parameters;
-
+
namespace Org.BouncyCastle.Crypto
{
/// Base interface for a symmetric key block cipher.
@@ -11,7 +10,7 @@ namespace Org.BouncyCastle.Crypto
/// Initialise the cipher.
/// Initialise for encryption if true, for decryption if false.
/// The key or other data required by the cipher.
- void Init(bool forEncryption, KeyParameter parameters);
+ void Init(bool forEncryption, ICipherParameters parameters);
/// The block size for this cipher, in bytes.
int GetBlockSize();
diff --git a/SharpCompress/Crypto/IBufferedCipher.cs b/SharpCompress/Crypto/IBufferedCipher.cs
new file mode 100644
index 00000000..207b9f09
--- /dev/null
+++ b/SharpCompress/Crypto/IBufferedCipher.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+ /// Block cipher engines are expected to conform to this interface.
+ public interface IBufferedCipher
+ {
+ /// The name of the algorithm this cipher implements.
+ string AlgorithmName { get; }
+
+ /// Initialise the cipher.
+ /// If true the cipher is initialised for encryption,
+ /// if false for decryption.
+ /// The key and other data required by the cipher.
+ void Init(bool forEncryption, ICipherParameters parameters);
+
+ int GetBlockSize();
+
+ int GetOutputSize(int inputLen);
+
+ int GetUpdateOutputSize(int inputLen);
+
+ byte[] ProcessByte(byte input);
+ int ProcessByte(byte input, byte[] output, int outOff);
+
+ byte[] ProcessBytes(byte[] input);
+ byte[] ProcessBytes(byte[] input, int inOff, int length);
+ int ProcessBytes(byte[] input, byte[] output, int outOff);
+ int ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff);
+
+ byte[] DoFinal();
+ byte[] DoFinal(byte[] input);
+ byte[] DoFinal(byte[] input, int inOff, int length);
+ int DoFinal(byte[] output, int outOff);
+ int DoFinal(byte[] input, byte[] output, int outOff);
+ int DoFinal(byte[] input, int inOff, int length, byte[] output, int outOff);
+
+ ///
+ /// Reset the cipher. After resetting the cipher is in the same state
+ /// as it was after the last init (if there was one).
+ ///
+ void Reset();
+ }
+}
diff --git a/SharpCompress/Crypto/ICipherParameters.cs b/SharpCompress/Crypto/ICipherParameters.cs
index 8bb0621a..995fd606 100644
--- a/SharpCompress/Crypto/ICipherParameters.cs
+++ b/SharpCompress/Crypto/ICipherParameters.cs
@@ -1,4 +1,5 @@
-namespace Org.BouncyCastle.Crypto
+
+namespace Org.BouncyCastle.Crypto
{
public interface ICipherParameters
{
diff --git a/SharpCompress/Crypto/IMac.cs b/SharpCompress/Crypto/IMac.cs
new file mode 100644
index 00000000..4305d6b0
--- /dev/null
+++ b/SharpCompress/Crypto/IMac.cs
@@ -0,0 +1,64 @@
+namespace Org.BouncyCastle.Crypto
+{
+ public interface IMac
+ {
+ /**
+ * Initialise the MAC.
+ *
+ * @param param the key and other data required by the MAC.
+ * @exception ArgumentException if the parameters argument is
+ * inappropriate.
+ */
+ void Init(ICipherParameters parameters);
+
+ /**
+ * Return the name of the algorithm the MAC implements.
+ *
+ * @return the name of the algorithm the MAC implements.
+ */
+ string AlgorithmName { get; }
+
+ /**
+ * Return the block size for this MAC (in bytes).
+ *
+ * @return the block size for this MAC in bytes.
+ */
+ int GetMacSize();
+
+ /**
+ * add a single byte to the mac for processing.
+ *
+ * @param in the byte to be processed.
+ * @exception InvalidOperationException if the MAC is not initialised.
+ */
+ void Update(byte input);
+
+ /**
+ * @param in the array containing the input.
+ * @param inOff the index in the array the data begins at.
+ * @param len the length of the input starting at inOff.
+ * @exception InvalidOperationException if the MAC is not initialised.
+ * @exception DataLengthException if there isn't enough data in in.
+ */
+ void BlockUpdate(byte[] input, int inOff, int len);
+
+ /**
+ * Compute the final stage of the MAC writing the output to the out
+ * parameter.
+ *
+ * doFinal leaves the MAC in the same state it was after the last init.
+ *