浏览代码

Import minimalistic BouncyCastle for ECDH

With some rewrites to avoid import of unneeded features.
Stefan Rinkes 7 年之前
父节点
当前提交
d24fe2006f
共有 81 个文件被更改,包括 19213 次插入6 次删除
  1. 26 0
      THIRD-PARTY-NOTICES.TXT
  2. 217 1
      src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj
  3. 1 1
      src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj
  4. 217 1
      src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj
  5. 1 1
      src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
  6. 217 1
      src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj
  7. 217 1
      src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj
  8. 72 0
      src/Renci.SshNet/Renci.SshNet.csproj
  9. 121 0
      src/Renci.SshNet/Security/BouncyCastle/asn1/sec/SECNamedCurves.cs
  10. 40 0
      src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9Curve.cs
  11. 93 0
      src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParameters.cs
  12. 25 0
      src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParametersHolder.cs
  13. 57 0
      src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECPoint.cs
  14. 33 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricCipherKeyPair.cs
  15. 42 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricKeyParameter.cs
  16. 9 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/IAsymmetricCipherKeyPairGenerator.cs
  17. 61 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/IDigest.cs
  18. 56 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/KeyGenerationParameters.cs
  19. 39 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/License.html
  20. 46 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/agreement/ECDHCBasicAgreement.cs
  21. 129 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/digests/GeneralDigest.cs
  22. 280 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/digests/Sha256Digest.cs
  23. 91 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/generators/ECKeyPairGenerator.cs
  24. 91 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECDomainParameters.cs
  25. 23 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyGenerationParameters.cs
  26. 79 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyParameters.cs
  27. 61 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPrivateKeyParameters.cs
  28. 60 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPublicKeyParameters.cs
  29. 73 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs
  30. 117 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/prng/DigestRandomGenerator.cs
  31. 26 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs
  32. 345 0
      src/Renci.SshNet/Security/BouncyCastle/crypto/util/Pack.cs
  33. 3601 0
      src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs
  34. 496 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/ECAlgorithms.cs
  35. 1278 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/ECCurve.cs
  36. 972 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/ECFieldElement.cs
  37. 10 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/ECLookupTable.cs
  38. 2122 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPoint.cs
  39. 9 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPointMap.cs
  40. 2206 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/LongArray.cs
  41. 241 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/SimpleBigDecimal.cs
  42. 845 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/Tnaf.cs
  43. 36 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/ZTauElement.cs
  44. 11 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/ECEndomorphism.cs
  45. 10 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs
  46. 29 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/AbstractECMultiplier.cs
  47. 18 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ECMultiplier.cs
  48. 58 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointCombMultiplier.cs
  49. 43 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointPreCompInfo.cs
  50. 95 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointUtilities.cs
  51. 40 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/GlvMultiplier.cs
  52. 9 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/IPreCompCallback.cs
  53. 11 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/PreCompInfo.cs
  54. 44 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ValidityPreCompInfo.cs
  55. 98 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafL2RMultiplier.cs
  56. 46 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafPreCompInfo.cs
  57. 579 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafUtilities.cs
  58. 138 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafMultiplier.cs
  59. 24 0
      src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafPreCompInfo.cs
  60. 54 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/FiniteFields.cs
  61. 46 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/GF2Polynomial.cs
  62. 63 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/GenericPolynomialExtensionField.cs
  63. 12 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/IExtensionField.cs
  64. 11 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/IFiniteField.cs
  65. 13 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomial.cs
  66. 8 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomialExtensionField.cs
  67. 42 0
      src/Renci.SshNet/Security/BouncyCastle/math/field/PrimeField.cs
  68. 185 0
      src/Renci.SshNet/Security/BouncyCastle/math/raw/Mod.cs
  69. 1153 0
      src/Renci.SshNet/Security/BouncyCastle/math/raw/Nat.cs
  70. 92 0
      src/Renci.SshNet/Security/BouncyCastle/security/DigestUtilities.cs
  71. 210 0
      src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs
  72. 36 0
      src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs
  73. 725 0
      src/Renci.SshNet/Security/BouncyCastle/util/Arrays.cs
  74. 95 0
      src/Renci.SshNet/Security/BouncyCastle/util/BigIntegers.cs
  75. 32 0
      src/Renci.SshNet/Security/BouncyCastle/util/Enums.cs
  76. 29 0
      src/Renci.SshNet/Security/BouncyCastle/util/IMemoable.cs
  77. 29 0
      src/Renci.SshNet/Security/BouncyCastle/util/Integers.cs
  78. 27 0
      src/Renci.SshNet/Security/BouncyCastle/util/MemoableResetException.cs
  79. 14 0
      src/Renci.SshNet/Security/BouncyCastle/util/Times.cs
  80. 129 0
      src/Renci.SshNet/Security/BouncyCastle/util/encoders/Hex.cs
  81. 174 0
      src/Renci.SshNet/Security/BouncyCastle/util/encoders/HexEncoder.cs

+ 26 - 0
THIRD-PARTY-NOTICES.TXT

@@ -0,0 +1,26 @@
+SSH.NET uses third-party libraries or other resources that may be
+distributed under licenses different than the SSH.NET software.
+
+License notice for BouncyCastle
+-------------------------------
+
+http://bouncycastle.org/csharp
+
+Copyright (c) 2000 - 2018 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 217 - 1
src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj

@@ -602,6 +602,222 @@
     <Compile Include="..\Renci.SshNet\Security\Algorithm.cs">
       <Link>Security\Algorithm.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\sec\SECNamedCurves.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9Curve.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParametersHolder.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricKeyParameter.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\GeneralDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\Sha256Digest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\KeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECDomainParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\DigestRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\IRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\util\Pack.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\util\Pack.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\BigInteger.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\BigInteger.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\Tnaf.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\ZTauElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECAlgorithms.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECCurve.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECFieldElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECLookupTable.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPointMap.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\ECEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\GlvEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\LongArray.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\LongArray.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\PreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\FiniteFields.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GenericPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GF2Polynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IFiniteField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\PrimeField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\PrimeField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Mod.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Mod.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Nat.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Nat.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\DigestUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\DigestUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecureRandom.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecureRandom.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecurityUtilityException.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Arrays.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Arrays.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\BigIntegers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\BigIntegers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\Hex.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\Hex.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\HexEncoder.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Enums.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Enums.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\IMemoable.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\IMemoable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Integers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Integers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\MemoableResetException.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\MemoableResetException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Times.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Times.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Security\CertificateHostAlgorithm.cs">
       <Link>Security\CertificateHostAlgorithm.cs</Link>
     </Compile>
@@ -981,4 +1197,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
+</Project>

+ 1 - 1
src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj

@@ -34,4 +34,4 @@
   <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
     <DefineConstants>FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP;FEATURE_STREAM_APM;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_TAP;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512</DefineConstants>
   </PropertyGroup>
-</Project>
+</Project>

+ 217 - 1
src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj

@@ -602,6 +602,222 @@
     <Compile Include="..\Renci.SshNet\Security\Algorithm.cs">
       <Link>Security\Algorithm.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\sec\SECNamedCurves.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9Curve.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParametersHolder.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricKeyParameter.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\GeneralDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\Sha256Digest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\KeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECDomainParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\DigestRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\IRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\util\Pack.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\util\Pack.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\BigInteger.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\BigInteger.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\Tnaf.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\ZTauElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECAlgorithms.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECCurve.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECFieldElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECLookupTable.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPointMap.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\ECEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\GlvEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\LongArray.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\LongArray.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\PreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\FiniteFields.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GenericPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GF2Polynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IFiniteField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\PrimeField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\PrimeField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Mod.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Mod.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Nat.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Nat.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\DigestUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\DigestUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecureRandom.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecureRandom.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecurityUtilityException.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Arrays.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Arrays.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\BigIntegers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\BigIntegers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\Hex.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\Hex.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\HexEncoder.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Enums.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Enums.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\IMemoable.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\IMemoable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Integers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Integers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\MemoableResetException.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\MemoableResetException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Times.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Times.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Security\CertificateHostAlgorithm.cs">
       <Link>Security\CertificateHostAlgorithm.cs</Link>
     </Compile>
@@ -980,4 +1196,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
+</Project>

+ 1 - 1
src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -717,4 +717,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
+</Project>

+ 217 - 1
src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj

@@ -666,6 +666,222 @@
     <Compile Include="..\Renci.SshNet\Security\Algorithm.cs">
       <Link>Security\Algorithm.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\sec\SECNamedCurves.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9Curve.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParametersHolder.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricKeyParameter.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\GeneralDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\Sha256Digest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\KeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECDomainParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\DigestRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\IRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\util\Pack.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\util\Pack.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\BigInteger.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\BigInteger.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\Tnaf.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\ZTauElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECAlgorithms.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECCurve.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECFieldElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECLookupTable.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPointMap.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\ECEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\GlvEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\LongArray.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\LongArray.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\PreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\FiniteFields.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GenericPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GF2Polynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IFiniteField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\PrimeField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\PrimeField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Mod.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Mod.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Nat.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Nat.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\DigestUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\DigestUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecureRandom.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecureRandom.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecurityUtilityException.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Arrays.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Arrays.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\BigIntegers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\BigIntegers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\Hex.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\Hex.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\HexEncoder.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Enums.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Enums.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\IMemoable.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\IMemoable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Integers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Integers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\MemoableResetException.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\MemoableResetException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Times.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Times.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Security\CertificateHostAlgorithm.cs">
       <Link>Security\CertificateHostAlgorithm.cs</Link>
     </Compile>
@@ -1034,4 +1250,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
+</Project>

+ 217 - 1
src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj

@@ -631,6 +631,222 @@
     <Compile Include="..\Renci.SshNet\Security\Algorithm.cs">
       <Link>Security\Algorithm.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\sec\SECNamedCurves.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9Curve.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECParametersHolder.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\asn1\x9\X9ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\AsymmetricKeyParameter.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\GeneralDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\digests\Sha256Digest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\IDigest.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\IDigest.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\KeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECDomainParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\DigestRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\prng\IRandomGenerator.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\crypto\util\Pack.cs">
+      <Link>Security\Cryptography\BouncyCastle\crypto\util\Pack.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\BigInteger.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\BigInteger.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\Tnaf.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\abc\ZTauElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECAlgorithms.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECCurve.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECFieldElement.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECLookupTable.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPoint.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\ECPointMap.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\ECEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\endo\GlvEndomorphism.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\LongArray.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\LongArray.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ECMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\PreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WNafUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\FiniteFields.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GenericPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\GF2Polynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IFiniteField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomial.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\IPolynomialExtensionField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\field\PrimeField.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\field\PrimeField.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Mod.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Mod.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\math\raw\Nat.cs">
+      <Link>Security\Cryptography\BouncyCastle\math\raw\Nat.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\DigestUtilities.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\DigestUtilities.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecureRandom.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecureRandom.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\security\SecurityUtilityException.cs">
+      <Link>Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Arrays.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Arrays.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\BigIntegers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\BigIntegers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\Hex.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\Hex.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\encoders\HexEncoder.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Enums.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Enums.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\IMemoable.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\IMemoable.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Integers.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Integers.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\MemoableResetException.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\MemoableResetException.cs</Link>
+    </Compile>
+    <Compile Include="..\Renci.SshNet\Security\BouncyCastle\util\Times.cs">
+      <Link>Security\Cryptography\BouncyCastle\util\Times.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Security\CertificateHostAlgorithm.cs">
       <Link>Security\CertificateHostAlgorithm.cs</Link>
     </Compile>
@@ -1007,4 +1223,4 @@
   <Target Name="AfterBuild">
   </Target>
   -->
-</Project>
+</Project>

+ 72 - 0
src/Renci.SshNet/Renci.SshNet.csproj

@@ -168,6 +168,78 @@
     <Compile Include="RemotePathNoneTransformation.cs" />
     <Compile Include="RemotePathShellQuoteTransformation.cs" />
     <Compile Include="RemotePathTransformation.cs" />
+    <Compile Include="Security\BouncyCastle\asn1\sec\SECNamedCurves.cs" />
+    <Compile Include="Security\BouncyCastle\asn1\x9\X9Curve.cs" />
+    <Compile Include="Security\BouncyCastle\asn1\x9\X9ECParameters.cs" />
+    <Compile Include="Security\BouncyCastle\asn1\x9\X9ECParametersHolder.cs" />
+    <Compile Include="Security\BouncyCastle\asn1\x9\X9ECPoint.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\AsymmetricKeyParameter.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\digests\GeneralDigest.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\digests\Sha256Digest.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\IDigest.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\KeyGenerationParameters.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\parameters\ECDomainParameters.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\parameters\ECKeyParameters.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\prng\DigestRandomGenerator.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\prng\IRandomGenerator.cs" />
+    <Compile Include="Security\BouncyCastle\crypto\util\Pack.cs" />
+    <Compile Include="Security\BouncyCastle\math\BigInteger.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\abc\Tnaf.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\abc\ZTauElement.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\ECAlgorithms.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\ECCurve.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\ECFieldElement.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\ECLookupTable.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\ECPoint.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\ECPointMap.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\endo\ECEndomorphism.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\endo\GlvEndomorphism.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\LongArray.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\ECMultiplier.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\PreCompInfo.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\WNafUtilities.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs" />
+    <Compile Include="Security\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\FiniteFields.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\GenericPolynomialExtensionField.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\GF2Polynomial.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\IExtensionField.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\IFiniteField.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\IPolynomial.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\IPolynomialExtensionField.cs" />
+    <Compile Include="Security\BouncyCastle\math\field\PrimeField.cs" />
+    <Compile Include="Security\BouncyCastle\math\raw\Mod.cs" />
+    <Compile Include="Security\BouncyCastle\math\raw\Nat.cs" />
+    <Compile Include="Security\BouncyCastle\security\DigestUtilities.cs" />
+    <Compile Include="Security\BouncyCastle\security\SecureRandom.cs" />
+    <Compile Include="Security\BouncyCastle\security\SecurityUtilityException.cs" />
+    <Compile Include="Security\BouncyCastle\util\Arrays.cs" />
+    <Compile Include="Security\BouncyCastle\util\BigIntegers.cs" />
+    <Compile Include="Security\BouncyCastle\util\encoders\Hex.cs" />
+    <Compile Include="Security\BouncyCastle\util\encoders\HexEncoder.cs" />
+    <Compile Include="Security\BouncyCastle\util\Enums.cs" />
+    <Compile Include="Security\BouncyCastle\util\IMemoable.cs" />
+    <Compile Include="Security\BouncyCastle\util\Integers.cs" />
+    <Compile Include="Security\BouncyCastle\util\MemoableResetException.cs" />
+    <Compile Include="Security\BouncyCastle\util\Times.cs" />
     <Compile Include="Security\Cryptography\HMACMD5.cs" />
     <Compile Include="Security\Cryptography\HMACSHA1.cs" />
     <Compile Include="Security\Cryptography\HMACSHA256.cs" />

+ 121 - 0
src/Renci.SshNet/Security/BouncyCastle/asn1/sec/SECNamedCurves.cs

@@ -0,0 +1,121 @@
+using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9;
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities.Encoders;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec
+{
+    internal sealed class SecNamedCurves
+    {
+        /*
+         * secp256r1
+         */
+        internal class Secp256r1Holder
+            : X9ECParametersHolder
+        {
+            private Secp256r1Holder() {}
+
+            internal static readonly X9ECParametersHolder Instance = new Secp256r1Holder();
+
+            protected override X9ECParameters CreateParameters()
+            {
+                // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1
+                BigInteger p = FromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF");
+                BigInteger a = FromHex("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC");
+                BigInteger b = FromHex("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B");
+                byte[] S = Hex.Decode("C49D360886E704936A6678E1139D26B7819F7E90");
+                BigInteger n = FromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
+                BigInteger h = BigInteger.One;
+
+                ECCurve curve = new FpCurve(p, a, b, n, h);
+                X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04"
+                    + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"
+                    + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"));
+
+                return new X9ECParameters(curve, G, n, h, S);
+            }
+        }
+
+        /*
+         * secp384r1
+         */
+        internal class Secp384r1Holder
+            : X9ECParametersHolder
+        {
+            private Secp384r1Holder() {}
+
+            internal static readonly X9ECParametersHolder Instance = new Secp384r1Holder();
+
+            protected override X9ECParameters CreateParameters()
+            {
+                // p = 2^384 - 2^128 - 2^96 + 2^32 - 1
+                BigInteger p = FromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF");
+                BigInteger a = FromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC");
+                BigInteger b = FromHex("B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF");
+                byte[] S = Hex.Decode("A335926AA319A27A1D00896A6773A4827ACDAC73");
+                BigInteger n = FromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
+                BigInteger h = BigInteger.One;
+
+                ECCurve curve = new FpCurve(p, a, b, n, h);
+                X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04"
+                    + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"
+                    + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F"));
+
+                return new X9ECParameters(curve, G, n, h, S);
+            }
+        }
+
+        /*
+         * secp521r1
+         */
+        internal class Secp521r1Holder
+            : X9ECParametersHolder
+        {
+            private Secp521r1Holder() {}
+
+            internal static readonly X9ECParametersHolder Instance = new Secp521r1Holder();
+
+            protected override X9ECParameters CreateParameters()
+            {
+                // p = 2^521 - 1
+                BigInteger p = FromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+                BigInteger a = FromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC");
+                BigInteger b = FromHex("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00");
+                byte[] S = Hex.Decode("D09E8800291CB85396CC6717393284AAA0DA64BA");
+                BigInteger n = FromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409");
+                BigInteger h = BigInteger.One;
+
+                ECCurve curve = new FpCurve(p, a, b, n, h);
+                X9ECPoint G = new X9ECPoint(curve, Hex.Decode("04"
+                    + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"
+                    + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650"));
+
+                return new X9ECParameters(curve, G, n, h, S);
+            }
+        }
+
+        public static X9ECParameters GetByName(
+            string name)
+        {
+            switch(name)
+            {
+                case "P-256":
+                case "secp256r1":
+                    return Secp256r1Holder.Instance.Parameters;
+                case "P-384":
+                case "secp384r1":
+                    return Secp384r1Holder.Instance.Parameters;
+                case "P-521":
+                case "secp521r1":
+                    return Secp521r1Holder.Instance.Parameters;
+            }
+
+            return null;
+        }
+
+        private static BigInteger FromHex(string hex)
+        {
+            return new BigInteger(1, Hex.Decode(hex));
+        }
+    }
+}

+ 40 - 0
src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9Curve.cs

@@ -0,0 +1,40 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9
+{
+    internal class X9Curve
+    {
+        private readonly ECCurve curve;
+        private readonly byte[] seed;
+
+        public X9Curve(
+            ECCurve curve)
+            : this(curve, null)
+        {
+        }
+
+        public X9Curve(
+            ECCurve	curve,
+            byte[]	seed)
+        {
+            if (curve == null)
+                throw new ArgumentNullException("curve");
+
+            this.curve = curve;
+            this.seed = Arrays.Clone(seed);
+        }
+
+        public ECCurve Curve
+        {
+            get { return curve; }
+        }
+
+        public byte[] GetSeed()
+        {
+            return Arrays.Clone(seed);
+        }
+    }
+}

+ 93 - 0
src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParameters.cs

@@ -0,0 +1,93 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9
+{
+    internal class X9ECParameters
+    {
+        private byte[]		seed;
+
+		public static X9ECParameters GetInstance(Object obj)
+		{
+			if (obj is X9ECParameters)
+				return (X9ECParameters)obj;
+
+            return null;
+		}
+
+        public X9ECParameters(
+            ECCurve		curve,
+            ECPoint		g,
+            BigInteger	n)
+            : this(curve, g, n, null, null)
+        {
+        }
+
+        public X9ECParameters(
+            ECCurve     curve,
+            X9ECPoint   g,
+            BigInteger  n,
+            BigInteger  h)
+            : this(curve, g, n, h, null)
+        {
+        }
+
+        public X9ECParameters(
+            ECCurve		curve,
+            ECPoint		g,
+            BigInteger	n,
+            BigInteger	h)
+            : this(curve, g, n, h, null)
+        {
+        }
+
+        public X9ECParameters(
+            ECCurve		curve,
+            ECPoint		g,
+            BigInteger	n,
+            BigInteger	h,
+            byte[]		seed)
+            : this(curve, new X9ECPoint(g), n, h, seed)
+        {
+        }
+
+        public X9ECParameters(
+            ECCurve     curve,
+            X9ECPoint   g,
+            BigInteger  n,
+            BigInteger  h,
+            byte[]      seed)
+        {
+            this.Curve = curve;
+            this.BaseEntry = g;
+            this.N = n;
+            this.H = h;
+            this.seed = seed;
+        }
+
+        public ECCurve Curve { get; private set; }
+
+        public ECPoint G
+        {
+            get { return BaseEntry.Point; }
+        }
+
+        public BigInteger N { get; private set; }
+
+        public BigInteger H { get; private set; }
+
+        public byte[] GetSeed()
+        {
+            return seed;
+        }
+
+        public X9Curve CurveEntry
+        {
+            get { return new X9Curve(Curve, seed); }
+        }
+
+        public X9ECPoint BaseEntry { get; private set; }
+    }
+}

+ 25 - 0
src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParametersHolder.cs

@@ -0,0 +1,25 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9
+{
+	internal abstract class X9ECParametersHolder
+	{
+		private X9ECParameters parameters;
+
+		public X9ECParameters Parameters
+		{
+			get
+			{
+                lock (this)
+                {
+                    if (parameters == null)
+                    {
+                        parameters = CreateParameters();
+                    }
+
+                    return parameters;
+                }
+            }
+        }
+
+		protected abstract X9ECParameters CreateParameters();
+	}
+}

+ 57 - 0
src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECPoint.cs

@@ -0,0 +1,57 @@
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9
+{
+    internal class X9ECPoint
+    {
+        private readonly byte[] encoding;
+
+        private ECCurve c;
+        private ECPoint p;
+
+        public X9ECPoint(ECPoint p)
+            : this(p, false)
+        {
+        }
+
+        public X9ECPoint(ECPoint p, bool compressed)
+        {
+            this.p = p.Normalize();
+            this.encoding = p.GetEncoded(compressed);
+        }
+
+        public X9ECPoint(ECCurve c, byte[] encoding)
+        {
+            this.c = c;
+            this.encoding = Arrays.Clone(encoding);
+        }
+
+        public byte[] GetPointEncoding()
+        {
+            return Arrays.Clone(encoding);
+        }
+
+        public ECPoint Point
+        {
+            get
+            {
+                if (p == null)
+                {
+                    p = c.DecodePoint(encoding).Normalize();
+                }
+
+                return p;
+            }
+        }
+
+        public bool IsPointCompressed
+        {
+            get
+            {
+                byte[] octets = encoding;
+                return octets != null && octets.Length > 0 && (octets[0] == 2 || octets[0] == 3);
+            }
+        }
+    }
+}

+ 33 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricCipherKeyPair.cs

@@ -0,0 +1,33 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto
+{
+    internal class AsymmetricCipherKeyPair
+    {
+        private readonly AsymmetricKeyParameter publicParameter;
+        private readonly AsymmetricKeyParameter privateParameter;
+
+        public AsymmetricCipherKeyPair(
+            AsymmetricKeyParameter    publicParameter,
+            AsymmetricKeyParameter    privateParameter)
+        {
+			if (publicParameter.IsPrivate)
+				throw new ArgumentException("Expected a public key", "publicParameter");
+			if (!privateParameter.IsPrivate)
+				throw new ArgumentException("Expected a private key", "privateParameter");
+
+			this.publicParameter = publicParameter;
+            this.privateParameter = privateParameter;
+        }
+
+        public AsymmetricKeyParameter Public
+        {
+            get { return publicParameter; }
+        }
+
+        public AsymmetricKeyParameter Private
+        {
+            get { return privateParameter; }
+        }
+    }
+}

+ 42 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricKeyParameter.cs

@@ -0,0 +1,42 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto
+{
+    internal abstract class AsymmetricKeyParameter
+    {
+        private readonly bool privateKey;
+
+        protected AsymmetricKeyParameter(
+            bool privateKey)
+        {
+            this.privateKey = privateKey;
+        }
+
+		public bool IsPrivate
+        {
+            get { return privateKey; }
+        }
+
+		public override bool Equals(
+			object obj)
+		{
+			AsymmetricKeyParameter other = obj as AsymmetricKeyParameter;
+
+			if (other == null)
+			{
+				return false;
+			}
+
+			return Equals(other);
+		}
+
+		protected bool Equals(
+			AsymmetricKeyParameter other)
+		{
+			return privateKey == other.privateKey;
+		}
+
+		public override int GetHashCode()
+		{
+			return privateKey.GetHashCode();
+		}
+    }
+}

+ 9 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/IAsymmetricCipherKeyPairGenerator.cs

@@ -0,0 +1,9 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto
+{
+    internal interface IAsymmetricCipherKeyPairGenerator
+    {
+        void Init(KeyGenerationParameters parameters);
+
+        AsymmetricCipherKeyPair GenerateKeyPair();
+    }
+}

+ 61 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/IDigest.cs

@@ -0,0 +1,61 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto
+{
+    /**
+     * interface that a message digest conforms to.
+     */
+    internal interface IDigest
+    {
+        /**
+         * return the algorithm name
+         *
+         * @return the algorithm name
+         */
+        string AlgorithmName { get; }
+
+		/**
+         * return the size, in bytes, of the digest produced by this message digest.
+         *
+         * @return the size, in bytes, of the digest produced by this message digest.
+         */
+		int GetDigestSize();
+
+		/**
+         * return the size, in bytes, of the internal buffer used by this digest.
+         *
+         * @return the size, in bytes, of the internal buffer used by this digest.
+         */
+		int GetByteLength();
+
+		/**
+         * update the message digest with a single byte.
+         *
+         * @param inByte the input byte to be entered.
+         */
+        void Update(byte input);
+
+        /**
+         * update the message digest with a block of bytes.
+         *
+         * @param input the byte array containing the data.
+         * @param inOff the offset into the byte array where the data starts.
+         * @param len the length of the data.
+         */
+        void BlockUpdate(byte[] input, int inOff, int length);
+
+        /**
+         * Close the digest, producing the final digest value. The doFinal
+         * call leaves the digest reset.
+         *
+         * @param output the array the digest is to be copied into.
+         * @param outOff the offset into the out array the digest is to start at.
+         */
+        int DoFinal(byte[] output, int outOff);
+
+        /**
+         * reset the digest back to it's initial state.
+         */
+        void Reset();
+    }
+}

+ 56 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/KeyGenerationParameters.cs

@@ -0,0 +1,56 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto
+{
+    /**
+     * The base class for parameters to key generators.
+     */
+    internal class KeyGenerationParameters
+    {
+        private SecureRandom	random;
+        private int				strength;
+
+        /**
+         * initialise the generator with a source of randomness
+         * and a strength (in bits).
+         *
+         * @param random the random byte source.
+         * @param strength the size, in bits, of the keys we want to produce.
+         */
+        public KeyGenerationParameters(
+            SecureRandom	random,
+            int				strength)
+        {
+			if (random == null)
+				throw new ArgumentNullException("random");
+			if (strength < 1)
+				throw new ArgumentException("strength must be a positive value", "strength");
+
+			this.random = random;
+            this.strength = strength;
+        }
+
+		/**
+         * return the random source associated with this
+         * generator.
+         *
+         * @return the generators random source.
+         */
+        public SecureRandom Random
+        {
+            get { return random; }
+        }
+
+		/**
+         * return the bit strength for keys produced by this generator,
+         *
+         * @return the strength of the keys this generator produces (in bits).
+         */
+        public int Strength
+        {
+            get { return strength; }
+        }
+    }
+}

+ 39 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/License.html

@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+  <meta content="text/html; charset=ISO-8859-1"
+ http-equiv="content-type">
+  <title>License</title>
+</head>
+<body>
+<h2>The Bouncy Castle Cryptographic C#&reg; API</h2>
+<h3>License:</h3>
+The Bouncy Castle License<br>
+Copyright (c) 2000-2018 The Legion of the Bouncy Castle Inc.
+(https://www.bouncycastle.org)<br>
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"), to deal in the
+Software without restriction, including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sub license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br>
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.<br>
+<span style="font-weight: bold;">THE SOFTWARE IS PROVIDED "AS IS",
+WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,</span><br
+ style="font-weight: bold;">
+<span style="font-weight: bold;">INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR</span><br
+ style="font-weight: bold;">
+<span style="font-weight: bold;">PURPOSE AND NONINFRINGEMENT. IN NO
+EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE</span><br
+ style="font-weight: bold;">
+<span style="font-weight: bold;">LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR</span><br
+ style="font-weight: bold;">
+<span style="font-weight: bold;">OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER</span><br
+ style="font-weight: bold;">
+<span style="font-weight: bold;">DEALINGS IN THE SOFTWARE.<br>
+<br>
+</span>
+</body>
+</html>

+ 46 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/agreement/ECDHCBasicAgreement.cs

@@ -0,0 +1,46 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Agreement
+{
+    internal class ECDHCBasicAgreement
+    {
+        private ECPrivateKeyParameters privKey;
+
+        public virtual void Init(
+            AsymmetricKeyParameter parameters)
+        {
+            this.privKey = (ECPrivateKeyParameters)parameters;
+        }
+
+        public virtual int GetFieldSize()
+        {
+            return (privKey.Parameters.Curve.FieldSize + 7) / 8;
+        }
+
+        public virtual BigInteger CalculateAgreement(
+            ECPublicKeyParameters pubKey)
+        {
+            ECPublicKeyParameters pub = pubKey;
+            ECDomainParameters dp = privKey.Parameters;
+            if (!dp.Equals(pub.Parameters))
+                throw new InvalidOperationException("ECDHC public key has wrong domain parameters");
+
+            BigInteger hd = dp.H.Multiply(privKey.D).Mod(dp.N);
+
+            // Always perform calculations on the exact curve specified by our private key's parameters
+            ECPoint pubPoint = ECAlgorithms.CleanPoint(dp.Curve, pub.Q);
+            if (pubPoint.IsInfinity)
+                throw new InvalidOperationException("Infinity is not a valid public key for ECDHC");
+
+            ECPoint P = pubPoint.Multiply(hd).Normalize();
+            if (P.IsInfinity)
+                throw new InvalidOperationException("Infinity is not a valid agreement value for ECDHC");
+
+            return P.AffineXCoord.ToBigInteger();
+        }
+    }
+}

+ 129 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/digests/GeneralDigest.cs

@@ -0,0 +1,129 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Digests
+{
+    internal abstract class GeneralDigest
+		: IDigest, IMemoable
+    {
+        private const int BYTE_LENGTH = 64;
+
+        private byte[]  xBuf;
+        private int     xBufOff;
+
+        private long    byteCount;
+
+        internal GeneralDigest()
+        {
+            xBuf = new byte[4];
+        }
+
+        internal GeneralDigest(GeneralDigest t)
+		{
+			xBuf = new byte[t.xBuf.Length];
+			CopyIn(t);
+		}
+
+		protected void CopyIn(GeneralDigest t)
+		{
+            Array.Copy(t.xBuf, 0, xBuf, 0, t.xBuf.Length);
+
+            xBufOff = t.xBufOff;
+            byteCount = t.byteCount;
+        }
+
+        public void Update(byte input)
+        {
+            xBuf[xBufOff++] = input;
+
+            if (xBufOff == xBuf.Length)
+            {
+                ProcessWord(xBuf, 0);
+                xBufOff = 0;
+            }
+
+            byteCount++;
+        }
+
+        public void BlockUpdate(
+            byte[]  input,
+            int     inOff,
+            int     length)
+        {
+            length = System.Math.Max(0, length);
+
+            //
+            // fill the current word
+            //
+            int i = 0;
+            if (xBufOff != 0)
+            {
+                while (i < length)
+                {
+                    xBuf[xBufOff++] = input[inOff + i++];
+                    if (xBufOff == 4)
+                    {
+                        ProcessWord(xBuf, 0);
+                        xBufOff = 0;
+                        break;
+                    }
+                }
+            }
+
+            //
+            // process whole words.
+            //
+            int limit = ((length - i) & ~3) + i;
+            for (; i < limit; i += 4)
+            {
+                ProcessWord(input, inOff + i);
+            }
+
+            //
+            // load in the remainder.
+            //
+            while (i < length)
+            {
+                xBuf[xBufOff++] = input[inOff + i++];
+            }
+
+            byteCount += length;
+        }
+
+        public void Finish()
+        {
+            long    bitLength = (byteCount << 3);
+
+            //
+            // add the pad bytes.
+            //
+            Update((byte)128);
+
+            while (xBufOff != 0) Update((byte)0);
+            ProcessLength(bitLength);
+            ProcessBlock();
+        }
+
+        public virtual void Reset()
+        {
+            byteCount = 0;
+            xBufOff = 0;
+			Array.Clear(xBuf, 0, xBuf.Length);
+        }
+
+		public int GetByteLength()
+		{
+			return BYTE_LENGTH;
+		}
+
+		internal abstract void ProcessWord(byte[] input, int inOff);
+        internal abstract void ProcessLength(long bitLength);
+        internal abstract void ProcessBlock();
+        public abstract string AlgorithmName { get; }
+		public abstract int GetDigestSize();
+        public abstract int DoFinal(byte[] output, int outOff);
+		public abstract IMemoable Copy();
+		public abstract void Reset(IMemoable t);
+    }
+}

+ 280 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/digests/Sha256Digest.cs

@@ -0,0 +1,280 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Utilities;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Digests
+{
+    internal class Sha256Digest
+		: GeneralDigest
+    {
+        private const int DigestLength = 32;
+
+        private uint H1, H2, H3, H4, H5, H6, H7, H8;
+        private uint[] X = new uint[64];
+        private int xOff;
+
+        public Sha256Digest()
+        {
+			initHs();
+        }
+
+        /**
+        * Copy constructor.  This will copy the state of the provided
+        * message digest.
+        */
+        public Sha256Digest(Sha256Digest t) : base(t)
+        {
+			CopyIn(t);
+		}
+
+		private void CopyIn(Sha256Digest t)
+		{
+			base.CopyIn(t);
+
+            H1 = t.H1;
+            H2 = t.H2;
+            H3 = t.H3;
+            H4 = t.H4;
+            H5 = t.H5;
+            H6 = t.H6;
+            H7 = t.H7;
+            H8 = t.H8;
+
+            Array.Copy(t.X, 0, X, 0, t.X.Length);
+            xOff = t.xOff;
+        }
+
+        public override string AlgorithmName
+		{
+			get { return "SHA-256"; }
+		}
+
+		public override int GetDigestSize()
+		{
+			return DigestLength;
+		}
+
+		internal override void ProcessWord(
+            byte[]  input,
+            int     inOff)
+		{
+			X[xOff] = Pack.BE_To_UInt32(input, inOff);
+
+			if (++xOff == 16)
+            {
+                ProcessBlock();
+            }
+        }
+
+		internal override void ProcessLength(
+            long bitLength)
+        {
+            if (xOff > 14)
+            {
+                ProcessBlock();
+            }
+
+            X[14] = (uint)((ulong)bitLength >> 32);
+            X[15] = (uint)((ulong)bitLength);
+        }
+
+        public override int DoFinal(
+            byte[]  output,
+            int     outOff)
+        {
+            Finish();
+
+            Pack.UInt32_To_BE((uint)H1, output, outOff);
+            Pack.UInt32_To_BE((uint)H2, output, outOff + 4);
+            Pack.UInt32_To_BE((uint)H3, output, outOff + 8);
+            Pack.UInt32_To_BE((uint)H4, output, outOff + 12);
+            Pack.UInt32_To_BE((uint)H5, output, outOff + 16);
+            Pack.UInt32_To_BE((uint)H6, output, outOff + 20);
+            Pack.UInt32_To_BE((uint)H7, output, outOff + 24);
+            Pack.UInt32_To_BE((uint)H8, output, outOff + 28);
+
+            Reset();
+
+            return DigestLength;
+        }
+
+        public override void Reset()
+        {
+            base.Reset();
+
+			initHs();
+
+            xOff = 0;
+			Array.Clear(X, 0, X.Length);
+        }
+
+		private void initHs()
+		{
+            /* SHA-256 initial hash value
+            * The first 32 bits of the fractional parts of the square roots
+            * of the first eight prime numbers
+            */
+            H1 = 0x6a09e667;
+            H2 = 0xbb67ae85;
+            H3 = 0x3c6ef372;
+            H4 = 0xa54ff53a;
+            H5 = 0x510e527f;
+            H6 = 0x9b05688c;
+            H7 = 0x1f83d9ab;
+            H8 = 0x5be0cd19;
+		}
+
+        internal override void ProcessBlock()
+        {
+            //
+            // expand 16 word block into 64 word blocks.
+            //
+            for (int ti = 16; ti <= 63; ti++)
+            {
+                X[ti] = Theta1(X[ti - 2]) + X[ti - 7] + Theta0(X[ti - 15]) + X[ti - 16];
+            }
+
+            //
+            // set up working variables.
+            //
+            uint a = H1;
+            uint b = H2;
+            uint c = H3;
+            uint d = H4;
+            uint e = H5;
+            uint f = H6;
+            uint g = H7;
+            uint h = H8;
+
+			int t = 0;
+			for(int i = 0; i < 8; ++i)
+			{
+				// t = 8 * i
+				h += Sum1Ch(e, f, g) + K[t] + X[t];
+				d += h;
+				h += Sum0Maj(a, b, c);
+				++t;
+
+				// t = 8 * i + 1
+				g += Sum1Ch(d, e, f) + K[t] + X[t];
+				c += g;
+				g += Sum0Maj(h, a, b);
+				++t;
+
+				// t = 8 * i + 2
+				f += Sum1Ch(c, d, e) + K[t] + X[t];
+				b += f;
+				f += Sum0Maj(g, h, a);
+				++t;
+
+				// t = 8 * i + 3
+				e += Sum1Ch(b, c, d) + K[t] + X[t];
+				a += e;
+				e += Sum0Maj(f, g, h);
+				++t;
+
+				// t = 8 * i + 4
+				d += Sum1Ch(a, b, c) + K[t] + X[t];
+				h += d;
+				d += Sum0Maj(e, f, g);
+				++t;
+
+				// t = 8 * i + 5
+				c += Sum1Ch(h, a, b) + K[t] + X[t];
+				g += c;
+				c += Sum0Maj(d, e, f);
+				++t;
+
+				// t = 8 * i + 6
+				b += Sum1Ch(g, h, a) + K[t] + X[t];
+				f += b;
+				b += Sum0Maj(c, d, e);
+				++t;
+
+				// t = 8 * i + 7
+				a += Sum1Ch(f, g, h) + K[t] + X[t];
+				e += a;
+				a += Sum0Maj(b, c, d);
+				++t;
+			}
+
+			H1 += a;
+            H2 += b;
+            H3 += c;
+            H4 += d;
+            H5 += e;
+            H6 += f;
+            H7 += g;
+            H8 += h;
+
+            //
+            // reset the offset and clean out the word buffer.
+            //
+            xOff = 0;
+			Array.Clear(X, 0, 16);
+        }
+
+		private static uint Sum1Ch(
+            uint    x,
+            uint    y,
+            uint    z)
+		{
+	        return (((x >> 6) | (x << 26)) ^ ((x >> 11) | (x << 21)) ^ ((x >> 25) | (x << 7)))
+				+ ((x & y) ^ ((~x) & z));
+		}
+
+		private static uint Sum0Maj(
+            uint	x,
+            uint    y,
+            uint    z)
+		{
+	        return (((x >> 2) | (x << 30)) ^ ((x >> 13) | (x << 19)) ^ ((x >> 22) | (x << 10)))
+				+ ((x & y) ^ (x & z) ^ (y & z));
+		}
+
+        private static uint Theta0(
+            uint x)
+        {
+	        return ((x >> 7) | (x << 25)) ^ ((x >> 18) | (x << 14)) ^ (x >> 3);
+        }
+
+        private static uint Theta1(
+            uint x)
+        {
+	        return ((x >> 17) | (x << 15)) ^ ((x >> 19) | (x << 13)) ^ (x >> 10);
+        }
+
+        private static readonly uint[] K = {
+            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+			0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+            0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+            0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+            0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+            0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+            0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+            0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+            0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+        };
+		
+		public override IMemoable Copy()
+		{
+			return new Sha256Digest(this);
+		}
+
+		public override void Reset(IMemoable other)
+		{
+			Sha256Digest d = (Sha256Digest)other;
+
+			CopyIn(d);
+		}
+
+    }
+}

+ 91 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/generators/ECKeyPairGenerator.cs

@@ -0,0 +1,91 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters;
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier;
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators
+{
+    internal class ECKeyPairGenerator
+        : IAsymmetricCipherKeyPairGenerator
+    {
+        private readonly string algorithm;
+
+        private ECDomainParameters parameters;
+        private SecureRandom random;
+
+        public ECKeyPairGenerator()
+            : this("EC")
+        {
+        }
+
+        public ECKeyPairGenerator(
+            string algorithm)
+        {
+            if (algorithm == null)
+                throw new ArgumentNullException("algorithm");
+
+            this.algorithm = ECKeyParameters.VerifyAlgorithmName(algorithm);
+        }
+
+        public void Init(
+            KeyGenerationParameters parameters)
+        {
+            if (parameters is ECKeyGenerationParameters)
+            {
+                ECKeyGenerationParameters ecP = (ECKeyGenerationParameters) parameters;
+
+                this.parameters = ecP.DomainParameters;
+            }
+
+            this.random = parameters.Random;
+
+            if (this.random == null)
+            {
+                this.random = new SecureRandom();
+            }
+        }
+
+        public AsymmetricCipherKeyPair GenerateKeyPair()
+        {
+            BigInteger n = parameters.N;
+            BigInteger d;
+            int minWeight = n.BitLength >> 2;
+
+            for (;;)
+            {
+                d = new BigInteger(n.BitLength, random);
+
+                if (d.CompareTo(BigInteger.Two) < 0 || d.CompareTo(n) >= 0)
+                    continue;
+
+                if (WNafUtilities.GetNafWeight(d) < minWeight)
+                    continue;
+
+                break;
+            }
+
+            ECPoint q = CreateBasePointMultiplier().Multiply(parameters.G, d);
+
+            return new AsymmetricCipherKeyPair(
+                new ECPublicKeyParameters(algorithm, q, parameters),
+                new ECPrivateKeyParameters(algorithm, d, parameters));
+        }
+
+        protected virtual ECMultiplier CreateBasePointMultiplier()
+        {
+            return new FixedPointCombMultiplier();
+        }
+
+        internal static ECPublicKeyParameters GetCorrespondingPublicKey(
+            ECPrivateKeyParameters privKey)
+        {
+            ECDomainParameters ec = privKey.Parameters;
+            ECPoint q = new FixedPointCombMultiplier().Multiply(ec.G, privKey.D);
+
+            return new ECPublicKeyParameters(privKey.AlgorithmName, q, ec);
+        }
+    }
+}

+ 91 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECDomainParameters.cs

@@ -0,0 +1,91 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters
+{
+    internal class ECDomainParameters
+    {
+        internal ECCurve     curve;
+        internal byte[]      seed;
+        internal ECPoint     g;
+        internal BigInteger  n;
+        internal BigInteger  h;
+        internal BigInteger  hInv;
+
+        public ECDomainParameters(
+            ECCurve     curve,
+            ECPoint     g,
+            BigInteger  n,
+            BigInteger  h,
+            byte[]      seed)
+        {
+            if (curve == null)
+                throw new ArgumentNullException("curve");
+            if (g == null)
+                throw new ArgumentNullException("g");
+            if (n == null)
+                throw new ArgumentNullException("n");
+            // we can't check for h == null here as h is optional in X9.62 as it is not required for ECDSA
+
+            this.curve = curve;
+            this.g = Validate(curve, g);
+            this.n = n;
+            this.h = h;
+            this.seed = Arrays.Clone(seed);
+        }
+
+        public ECCurve Curve
+        {
+            get { return curve; }
+        }
+
+        public ECPoint G
+        {
+            get { return g; }
+        }
+
+        public BigInteger N
+        {
+            get { return n; }
+        }
+
+        public BigInteger H
+        {
+            get { return h; }
+        }
+
+        public BigInteger HInv
+        {
+            get
+            {
+                lock (this)
+                {
+                    if (hInv == null)
+                    {
+                        hInv = h.ModInverse(n);
+                    }
+                    return hInv;
+                }
+            }
+        }
+
+        internal static ECPoint Validate(ECCurve c, ECPoint q)
+        {
+            if (q == null)
+                throw new ArgumentException("Point has null value", "q");
+
+            q = ECAlgorithms.ImportPoint(c, q).Normalize();
+
+            if (q.IsInfinity)
+                throw new ArgumentException("Point at infinity", "q");
+
+            if (!q.IsValid())
+                throw new ArgumentException("Point not on curve", "q");
+
+            return q;
+        }
+    }
+}

+ 23 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyGenerationParameters.cs

@@ -0,0 +1,23 @@
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters
+{
+    internal class ECKeyGenerationParameters
+		: KeyGenerationParameters
+    {
+        private readonly ECDomainParameters domainParams;
+
+		public ECKeyGenerationParameters(
+			ECDomainParameters	domainParameters,
+			SecureRandom		random)
+			: base(random, domainParameters.N.BitLength)
+        {
+            this.domainParams = domainParameters;
+        }
+
+		public ECDomainParameters DomainParameters
+        {
+			get { return domainParams; }
+        }
+    }
+}

+ 79 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyParameters.cs

@@ -0,0 +1,79 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters
+{
+    internal abstract class ECKeyParameters
+        : AsymmetricKeyParameter
+    {
+        private static readonly string[] algorithms = { "EC", "ECDH" };
+
+        private readonly string algorithm;
+        private readonly ECDomainParameters parameters;
+
+        protected ECKeyParameters(
+            string				algorithm,
+            bool				isPrivate,
+            ECDomainParameters	parameters)
+            : base(isPrivate)
+        {
+            if (algorithm == null)
+                throw new ArgumentNullException("algorithm");
+            if (parameters == null)
+                throw new ArgumentNullException("parameters");
+
+            this.algorithm = VerifyAlgorithmName(algorithm);
+            this.parameters = parameters;
+        }
+
+        public string AlgorithmName
+        {
+            get { return algorithm; }
+        }
+
+        public ECDomainParameters Parameters
+        {
+            get { return parameters; }
+        }
+
+        public override bool Equals(
+            object obj)
+        {
+            if (obj == this)
+                return true;
+
+            ECDomainParameters other = obj as ECDomainParameters;
+
+            if (other == null)
+                return false;
+
+            return Equals(other);
+        }
+
+        protected bool Equals(
+            ECKeyParameters other)
+        {
+            return parameters.Equals(other.parameters) && base.Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            return parameters.GetHashCode() ^ base.GetHashCode();
+        }
+
+        internal ECKeyGenerationParameters CreateKeyGenerationParameters(
+            SecureRandom random)
+        {
+            return new ECKeyGenerationParameters(parameters, random);
+        }
+
+        internal static string VerifyAlgorithmName(string algorithm)
+        {
+            if (Array.IndexOf(algorithms, algorithm, 0, algorithms.Length) < 0)
+                throw new ArgumentException("unrecognised algorithm: " + algorithm, "algorithm");
+            return algorithm.ToUpper();
+        }
+    }
+}

+ 61 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPrivateKeyParameters.cs

@@ -0,0 +1,61 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters
+{
+    internal class ECPrivateKeyParameters
+        : ECKeyParameters
+    {
+        private readonly BigInteger d;
+
+        public ECPrivateKeyParameters(
+            BigInteger			d,
+            ECDomainParameters	parameters)
+            : this("EC", d, parameters)
+        {
+        }
+
+        public ECPrivateKeyParameters(
+            string				algorithm,
+            BigInteger			d,
+            ECDomainParameters	parameters)
+            : base(algorithm, true, parameters)
+        {
+            if (d == null)
+                throw new ArgumentNullException("d");
+
+            this.d = d;
+        }
+
+        public BigInteger D
+        {
+            get { return d; }
+        }
+
+        public override bool Equals(
+            object obj)
+        {
+            if (obj == this)
+                return true;
+
+            ECPrivateKeyParameters other = obj as ECPrivateKeyParameters;
+
+            if (other == null)
+                return false;
+
+            return Equals(other);
+        }
+
+        protected bool Equals(
+            ECPrivateKeyParameters other)
+        {
+            return d.Equals(other.d) && base.Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            return d.GetHashCode() ^ base.GetHashCode();
+        }
+    }
+}

+ 60 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPublicKeyParameters.cs

@@ -0,0 +1,60 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters
+{
+    internal class ECPublicKeyParameters
+        : ECKeyParameters
+    {
+        private readonly ECPoint q;
+
+        public ECPublicKeyParameters(
+            ECPoint				q,
+            ECDomainParameters	parameters)
+            : this("EC", q, parameters)
+        {
+        }
+
+        public ECPublicKeyParameters(
+            string				algorithm,
+            ECPoint				q,
+            ECDomainParameters	parameters)
+            : base(algorithm, false, parameters)
+        {
+            if (q == null)
+                throw new ArgumentNullException("q");
+
+            this.q = ECDomainParameters.Validate(Parameters.Curve, q);
+        }
+
+        public ECPoint Q
+        {
+            get { return q; }
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (obj == this)
+                return true;
+
+            ECPublicKeyParameters other = obj as ECPublicKeyParameters;
+
+            if (other == null)
+                return false;
+
+            return Equals(other);
+        }
+
+        protected bool Equals(
+            ECPublicKeyParameters other)
+        {
+            return q.Equals(other.q) && base.Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            return q.GetHashCode() ^ base.GetHashCode();
+        }
+    }
+}

+ 73 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs

@@ -0,0 +1,73 @@
+#if !(NETCF_1_0 || PORTABLE)
+
+using System;
+using System.Security.Cryptography;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng
+{
+    internal class CryptoApiRandomGenerator
+        : IRandomGenerator
+    {
+        private readonly RandomNumberGenerator rndProv;
+
+        public CryptoApiRandomGenerator()
+#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP
+            : this(Abstractions.CryptoAbstraction.CreateRandomNumberGenerator())
+#endif
+        {
+        }
+
+        public CryptoApiRandomGenerator(RandomNumberGenerator rng)
+        {
+            this.rndProv = rng;
+        }
+
+        #region IRandomGenerator Members
+
+        public virtual void AddSeedMaterial(byte[] seed)
+        {
+            // We don't care about the seed
+        }
+
+        public virtual void AddSeedMaterial(long seed)
+        {
+            // We don't care about the seed
+        }
+
+        public virtual void NextBytes(byte[] bytes)
+        {
+#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP
+            rndProv.GetBytes(bytes);
+#else
+            if (bytes == null)
+                throw new ArgumentNullException("bytes");
+
+            var buffer = Windows.Security.Cryptography.CryptographicBuffer.GenerateRandom((uint)bytes.Length);
+            System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.CopyTo(buffer, bytes);
+#endif
+        }
+
+        public virtual void NextBytes(byte[] bytes, int start, int len)
+        {
+            if (start < 0)
+                throw new ArgumentException("Start offset cannot be negative", "start");
+            if (bytes.Length < (start + len))
+                throw new ArgumentException("Byte array too small for requested offset and length");
+
+            if (bytes.Length == len && start == 0) 
+            {
+                NextBytes(bytes);
+            }
+            else 
+            {
+                byte[] tmpBuf = new byte[len];
+                NextBytes(tmpBuf);
+                Array.Copy(tmpBuf, 0, bytes, start, len);
+            }
+        }
+
+        #endregion
+    }
+}
+
+#endif

+ 117 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/prng/DigestRandomGenerator.cs

@@ -0,0 +1,117 @@
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng
+{
+	internal class DigestRandomGenerator
+		: IRandomGenerator
+	{
+		private const long CYCLE_COUNT = 10;
+
+		private long	stateCounter;
+		private long	seedCounter;
+		private IDigest	digest;
+		private byte[]	state;
+		private byte[]	seed;
+
+		public DigestRandomGenerator(
+			IDigest digest)
+		{
+			this.digest = digest;
+
+			this.seed = new byte[digest.GetDigestSize()];
+			this.seedCounter = 1;
+
+			this.state = new byte[digest.GetDigestSize()];
+			this.stateCounter = 1;
+		}
+
+		public void AddSeedMaterial(
+			byte[] inSeed)
+		{
+			lock (this)
+			{
+				DigestUpdate(inSeed);
+				DigestUpdate(seed);
+				DigestDoFinal(seed);
+			}
+		}
+
+		public void AddSeedMaterial(
+			long rSeed)
+		{
+			lock (this)
+			{
+				DigestAddCounter(rSeed);
+				DigestUpdate(seed);
+				DigestDoFinal(seed);
+			}
+		}
+
+		public void NextBytes(
+			byte[] bytes)
+		{
+			NextBytes(bytes, 0, bytes.Length);
+		}
+
+		public void NextBytes(
+			byte[]	bytes,
+			int		start,
+			int		len)
+		{
+			lock (this)
+			{
+				int stateOff = 0;
+
+				GenerateState();
+
+				int end = start + len;
+				for (int i = start; i < end; ++i)
+				{
+					if (stateOff == state.Length)
+					{
+						GenerateState();
+						stateOff = 0;
+					}
+					bytes[i] = state[stateOff++];
+				}
+			}
+		}
+
+		private void CycleSeed()
+		{
+			DigestUpdate(seed);
+			DigestAddCounter(seedCounter++);
+			DigestDoFinal(seed);
+		}
+
+		private void GenerateState()
+		{
+			DigestAddCounter(stateCounter++);
+			DigestUpdate(state);
+			DigestUpdate(seed);
+			DigestDoFinal(state);
+
+			if ((stateCounter % CYCLE_COUNT) == 0)
+			{
+				CycleSeed();
+			}
+		}
+
+		private void DigestAddCounter(long seedVal)
+		{
+            byte[] bytes = new byte[8];
+            Pack.UInt64_To_LE((ulong)seedVal, bytes);
+            digest.BlockUpdate(bytes, 0, bytes.Length);
+		}
+
+        private void DigestUpdate(byte[] inSeed)
+		{
+			digest.BlockUpdate(inSeed, 0, inSeed.Length);
+		}
+
+		private void DigestDoFinal(byte[] result)
+		{
+			digest.DoFinal(result, 0);
+		}
+	}
+}

+ 26 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs

@@ -0,0 +1,26 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng
+{
+	/// <remarks>Generic interface for objects generating random bytes.</remarks>
+	internal interface IRandomGenerator
+	{
+		/// <summary>Add more seed material to the generator.</summary>
+		/// <param name="seed">A byte array to be mixed into the generator's state.</param>
+		void AddSeedMaterial(byte[] seed);
+
+		/// <summary>Add more seed material to the generator.</summary>
+		/// <param name="seed">A long value to be mixed into the generator's state.</param>
+		void AddSeedMaterial(long seed);
+
+		/// <summary>Fill byte array with random values.</summary>
+		/// <param name="bytes">Array to be filled.</param>
+		void NextBytes(byte[] bytes);
+
+		/// <summary>Fill byte array with random values.</summary>
+		/// <param name="bytes">Array to receive bytes.</param>
+		/// <param name="start">Index to start filling at.</param>
+		/// <param name="len">Length of segment to fill.</param>
+		void NextBytes(byte[] bytes, int start, int len);
+	}
+}

+ 345 - 0
src/Renci.SshNet/Security/BouncyCastle/crypto/util/Pack.cs

@@ -0,0 +1,345 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Utilities
+{
+    internal sealed class Pack
+    {
+        private Pack()
+        {
+        }
+
+        internal static void UInt16_To_BE(ushort n, byte[] bs)
+        {
+            bs[0] = (byte)(n >> 8);
+            bs[1] = (byte)(n);
+        }
+
+        internal static void UInt16_To_BE(ushort n, byte[] bs, int off)
+        {
+            bs[off] = (byte)(n >> 8);
+            bs[off + 1] = (byte)(n);
+        }
+
+        internal static ushort BE_To_UInt16(byte[] bs)
+        {
+            uint n = (uint)bs[0] << 8
+                | (uint)bs[1];
+            return (ushort)n;
+        }
+
+        internal static ushort BE_To_UInt16(byte[] bs, int off)
+        {
+            uint n = (uint)bs[off] << 8
+                | (uint)bs[off + 1];
+            return (ushort)n;
+        }
+
+        internal static byte[] UInt32_To_BE(uint n)
+        {
+            byte[] bs = new byte[4];
+            UInt32_To_BE(n, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt32_To_BE(uint n, byte[] bs)
+        {
+            bs[0] = (byte)(n >> 24);
+            bs[1] = (byte)(n >> 16);
+            bs[2] = (byte)(n >> 8);
+            bs[3] = (byte)(n);
+        }
+
+        internal static void UInt32_To_BE(uint n, byte[] bs, int off)
+        {
+            bs[off] = (byte)(n >> 24);
+            bs[off + 1] = (byte)(n >> 16);
+            bs[off + 2] = (byte)(n >> 8);
+            bs[off + 3] = (byte)(n);
+        }
+
+        internal static byte[] UInt32_To_BE(uint[] ns)
+        {
+            byte[] bs = new byte[4 * ns.Length];
+            UInt32_To_BE(ns, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt32_To_BE(uint[] ns, byte[] bs, int off)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                UInt32_To_BE(ns[i], bs, off);
+                off += 4;
+            }
+        }
+
+        internal static uint BE_To_UInt32(byte[] bs)
+        {
+            return (uint)bs[0] << 24
+                | (uint)bs[1] << 16
+                | (uint)bs[2] << 8
+                | (uint)bs[3];
+        }
+
+        internal static uint BE_To_UInt32(byte[] bs, int off)
+        {
+            return (uint)bs[off] << 24
+                | (uint)bs[off + 1] << 16
+                | (uint)bs[off + 2] << 8
+                | (uint)bs[off + 3];
+        }
+
+        internal static void BE_To_UInt32(byte[] bs, int off, uint[] ns)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                ns[i] = BE_To_UInt32(bs, off);
+                off += 4;
+            }
+        }
+
+        internal static byte[] UInt64_To_BE(ulong n)
+        {
+            byte[] bs = new byte[8];
+            UInt64_To_BE(n, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt64_To_BE(ulong n, byte[] bs)
+        {
+            UInt32_To_BE((uint)(n >> 32), bs);
+            UInt32_To_BE((uint)(n), bs, 4);
+        }
+
+        internal static void UInt64_To_BE(ulong n, byte[] bs, int off)
+        {
+            UInt32_To_BE((uint)(n >> 32), bs, off);
+            UInt32_To_BE((uint)(n), bs, off + 4);
+        }
+
+        internal static byte[] UInt64_To_BE(ulong[] ns)
+        {
+            byte[] bs = new byte[8 * ns.Length];
+            UInt64_To_BE(ns, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt64_To_BE(ulong[] ns, byte[] bs, int off)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                UInt64_To_BE(ns[i], bs, off);
+                off += 8;
+            }
+        }
+
+        internal static ulong BE_To_UInt64(byte[] bs)
+        {
+            uint hi = BE_To_UInt32(bs);
+            uint lo = BE_To_UInt32(bs, 4);
+            return ((ulong)hi << 32) | (ulong)lo;
+        }
+
+        internal static ulong BE_To_UInt64(byte[] bs, int off)
+        {
+            uint hi = BE_To_UInt32(bs, off);
+            uint lo = BE_To_UInt32(bs, off + 4);
+            return ((ulong)hi << 32) | (ulong)lo;
+        }
+
+        internal static void BE_To_UInt64(byte[] bs, int off, ulong[] ns)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                ns[i] = BE_To_UInt64(bs, off);
+                off += 8;
+            }
+        }
+
+        internal static void UInt16_To_LE(ushort n, byte[] bs)
+        {
+            bs[0] = (byte)(n);
+            bs[1] = (byte)(n >> 8);
+        }
+
+        internal static void UInt16_To_LE(ushort n, byte[] bs, int off)
+        {
+            bs[off] = (byte)(n);
+            bs[off + 1] = (byte)(n >> 8);
+        }
+
+        internal static ushort LE_To_UInt16(byte[] bs)
+        {
+            uint n = (uint)bs[0]
+                | (uint)bs[1] << 8;
+            return (ushort)n;
+        }
+
+        internal static ushort LE_To_UInt16(byte[] bs, int off)
+        {
+            uint n = (uint)bs[off]
+                | (uint)bs[off + 1] << 8;
+            return (ushort)n;
+        }
+
+        internal static byte[] UInt32_To_LE(uint n)
+        {
+            byte[] bs = new byte[4];
+            UInt32_To_LE(n, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt32_To_LE(uint n, byte[] bs)
+        {
+            bs[0] = (byte)(n);
+            bs[1] = (byte)(n >> 8);
+            bs[2] = (byte)(n >> 16);
+            bs[3] = (byte)(n >> 24);
+        }
+
+        internal static void UInt32_To_LE(uint n, byte[] bs, int off)
+        {
+            bs[off] = (byte)(n);
+            bs[off + 1] = (byte)(n >> 8);
+            bs[off + 2] = (byte)(n >> 16);
+            bs[off + 3] = (byte)(n >> 24);
+        }
+
+        internal static byte[] UInt32_To_LE(uint[] ns)
+        {
+            byte[] bs = new byte[4 * ns.Length];
+            UInt32_To_LE(ns, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt32_To_LE(uint[] ns, byte[] bs, int off)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                UInt32_To_LE(ns[i], bs, off);
+                off += 4;
+            }
+        }
+
+        internal static uint LE_To_UInt32(byte[] bs)
+        {
+            return (uint)bs[0]
+                | (uint)bs[1] << 8
+                | (uint)bs[2] << 16
+                | (uint)bs[3] << 24;
+        }
+
+        internal static uint LE_To_UInt32(byte[] bs, int off)
+        {
+            return (uint)bs[off]
+                | (uint)bs[off + 1] << 8
+                | (uint)bs[off + 2] << 16
+                | (uint)bs[off + 3] << 24;
+        }
+
+        internal static void LE_To_UInt32(byte[] bs, int off, uint[] ns)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                ns[i] = LE_To_UInt32(bs, off);
+                off += 4;
+            }
+        }
+
+        internal static void LE_To_UInt32(byte[] bs, int bOff, uint[] ns, int nOff, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                ns[nOff + i] = LE_To_UInt32(bs, bOff);
+                bOff += 4;
+            }
+        }
+
+        internal static uint[] LE_To_UInt32(byte[] bs, int off, int count)
+        {
+            uint[] ns = new uint[count];
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                ns[i] = LE_To_UInt32(bs, off);
+                off += 4;
+            }
+            return ns;
+        }
+
+        internal static byte[] UInt64_To_LE(ulong n)
+        {
+            byte[] bs = new byte[8];
+            UInt64_To_LE(n, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt64_To_LE(ulong n, byte[] bs)
+        {
+            UInt32_To_LE((uint)(n), bs);
+            UInt32_To_LE((uint)(n >> 32), bs, 4);
+        }
+
+        internal static void UInt64_To_LE(ulong n, byte[] bs, int off)
+        {
+            UInt32_To_LE((uint)(n), bs, off);
+            UInt32_To_LE((uint)(n >> 32), bs, off + 4);
+        }
+
+        internal static byte[] UInt64_To_LE(ulong[] ns)
+        {
+            byte[] bs = new byte[8 * ns.Length];
+            UInt64_To_LE(ns, bs, 0);
+            return bs;
+        }
+
+        internal static void UInt64_To_LE(ulong[] ns, byte[] bs, int off)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                UInt64_To_LE(ns[i], bs, off);
+                off += 8;
+            }
+        }
+
+        internal static void UInt64_To_LE(ulong[] ns, int nsOff, int nsLen, byte[] bs, int bsOff)
+        {
+            for (int i = 0; i < nsLen; ++i)
+            {
+                UInt64_To_LE(ns[nsOff + i], bs, bsOff);
+                bsOff += 8;
+            }
+        }
+
+        internal static ulong LE_To_UInt64(byte[] bs)
+        {
+            uint lo = LE_To_UInt32(bs);
+            uint hi = LE_To_UInt32(bs, 4);
+            return ((ulong)hi << 32) | (ulong)lo;
+        }
+
+        internal static ulong LE_To_UInt64(byte[] bs, int off)
+        {
+            uint lo = LE_To_UInt32(bs, off);
+            uint hi = LE_To_UInt32(bs, off + 4);
+            return ((ulong)hi << 32) | (ulong)lo;
+        }
+
+        internal static void LE_To_UInt64(byte[] bs, int off, ulong[] ns)
+        {
+            for (int i = 0; i < ns.Length; ++i)
+            {
+                ns[i] = LE_To_UInt64(bs, off);
+                off += 8;
+            }
+        }
+
+        internal static void LE_To_UInt64(byte[] bs, int bsOff, ulong[] ns, int nsOff, int nsLen)
+        {
+            for (int i = 0; i < nsLen; ++i)
+            {
+                ns[nsOff + i] = LE_To_UInt64(bs, bsOff);
+                bsOff += 8;
+            }
+        }
+    }
+}

+ 3601 - 0
src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs

@@ -0,0 +1,3601 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Text;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE)
+    [Serializable]
+#endif
+    internal class BigInteger
+    {
+        // The first few odd primes
+        /*
+                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 1033 1039 1049 1051
+            1061 1063 1069 1087 1091 1093 1097 1103
+            1109 1117 1123 1129 1151 1153 1163 1171
+            1181 1187 1193 1201 1213 1217 1223 1229
+            1231 1237 1249 1259 1277 1279 1283 1289
+        */
+
+        // Each list has a product < 2^31
+        internal 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 },
+            new int[]{ 1033, 1039, 1049 },
+            new int[]{ 1051, 1061, 1063 },
+            new int[]{ 1069, 1087, 1091 },
+
+            new int[]{ 1093, 1097, 1103 },
+            new int[]{ 1109, 1117, 1123 },
+            new int[]{ 1129, 1151, 1153 },
+            new int[]{ 1163, 1171, 1181 },
+            new int[]{ 1187, 1193, 1201 },
+
+            new int[]{ 1213, 1217, 1223 },
+            new int[]{ 1229, 1231, 1237 },
+            new int[]{ 1249, 1259, 1277 },
+            new int[]{ 1279, 1283, 1289 },
+        };
+
+        internal static readonly int[] primeProducts;
+
+        private const long IMASK = 0xFFFFFFFFL;
+        private const ulong UIMASK = 0xFFFFFFFFUL;
+
+        private static readonly int[] ZeroMagnitude = new int[0];
+        private static readonly byte[] ZeroEncoding = new byte[0];
+
+        private static readonly BigInteger[] SMALL_CONSTANTS = new BigInteger[17];
+        public static readonly BigInteger Zero;
+        public static readonly BigInteger One;
+        public static readonly BigInteger Two;
+        public static readonly BigInteger Three;
+        public static readonly BigInteger Ten;
+
+        //private readonly static byte[] BitCountTable =
+        //{
+        //    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 readonly static byte[] BitLengthTable =
+        {
+            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
+        };
+
+        // TODO Parse radix-2 64 bits at a time and radix-8 63 bits at a time
+        private const int chunk2 = 1, chunk8 = 1, chunk10 = 19, chunk16 = 16;
+        private static readonly BigInteger radix2, radix2E, radix8, radix8E, radix10, radix10E, radix16, radix16E;
+
+        private static readonly SecureRandom RandomSource = new SecureRandom();
+
+        /*
+         * These are the threshold bit-lengths (of an exponent) where we increase the window size.
+         * They are calculated according to the expected savings in multiplications.
+         * Some squares will also be saved on average, but we offset these against the extra storage costs.
+         */
+        private static readonly int[] ExpWindowThresholds = { 7, 25, 81, 241, 673, 1793, 4609, Int32.MaxValue };
+
+        private const int BitsPerByte = 8;
+        private const int BitsPerInt = 32;
+        private const int BytesPerInt = 4;
+
+        static BigInteger()
+        {
+            Zero = new BigInteger(0, ZeroMagnitude, false);
+            Zero.nBits = 0; Zero.nBitLength = 0;
+
+            SMALL_CONSTANTS[0] = Zero;
+            for (uint i = 1; i < SMALL_CONSTANTS.Length; ++i)
+            {
+                SMALL_CONSTANTS[i] = CreateUValueOf(i);
+            }
+
+            One = SMALL_CONSTANTS[1];
+            Two = SMALL_CONSTANTS[2];
+            Three = SMALL_CONSTANTS[3];
+            Ten = SMALL_CONSTANTS[10];
+
+            radix2 = ValueOf(2);
+            radix2E = radix2.Pow(chunk2);
+
+            radix8 = ValueOf(8);
+            radix8E = radix8.Pow(chunk8);
+
+            radix10 = ValueOf(10);
+            radix10E = radix10.Pow(chunk10);
+
+            radix16 = ValueOf(16);
+            radix16E = radix16.Pow(chunk16);
+
+            primeProducts = new int[primeLists.Length];
+
+            for (int i = 0; i < primeLists.Length; ++i)
+            {
+                int[] primeList = primeLists[i];
+                int product = primeList[0];
+                for (int j = 1; j < primeList.Length; ++j)
+                {
+                    product *= primeList[j];
+                }
+                primeProducts[i] = product;
+            }
+        }
+
+        private int[] magnitude; // array of ints with [0] being the most significant
+        private int sign; // -1 means -ve; +1 means +ve; 0 means 0;
+        private int nBits = -1; // cache BitCount() value
+        private int nBitLength = -1; // cache BitLength() value
+        private int mQuote = 0; // -m^(-1) mod b, b = 2^32 (see Montgomery mult.), 0 when uninitialised
+
+        private static int GetByteLength(
+            int nBits)
+        {
+            return (nBits + BitsPerByte - 1) / BitsPerByte;
+        }
+
+        internal static BigInteger Arbitrary(int sizeInBits)
+        {
+            return new BigInteger(sizeInBits, RandomSource);
+        }
+
+        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 8:
+                    // Is there anyway to restrict to octal digits?
+                    style = NumberStyles.Integer;
+                    chunk = chunk8;
+                    r = radix8;
+                    rE = radix8E;
+                    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, 8, 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 >= 2)
+                                throw new FormatException("Bad character in radix 2 string: " + s);
+
+                            // TODO Parse 64 bits at a time
+                            b = b.ShiftLeft(1);
+                            break;
+                        case 8:
+                            // TODO Need this because we are parsing in radix 10 above
+                            if (i >= 8)
+                                throw new FormatException("Bad character in radix 8 string: " + s);
+
+                            // TODO Parse 63 bits at a time
+                            b = b.ShiftLeft(3);
+                            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 == 8)
+                    {
+                        // 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 * 3);
+                    }
+                    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
+            int xBits = BitsPerByte * nBytes - sizeInBits;
+            b[0] &= (byte)(255U >> xBits);
+
+            this.magnitude = MakeMagnitude(b, 0, b.Length);
+            this.sign = this.magnitude.Length < 1 ? 0 : 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 = (byte)(255U >> xBits);
+            byte lead = (byte)(1 << (7 - 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] |= lead;
+
+                // 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 = 0;
+
+                if (certainty < 1)
+                    break;
+
+                if (CheckProbablePrime(certainty, random, true))
+                    break;
+
+                for (int j = 1; j < (magnitude.Length - 1); ++j)
+                {
+                    this.magnitude[j] ^= random.Next();
+
+                    if (CheckProbablePrime(certainty, random, true))
+                        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 += BitCnt(magnitude[i]);
+                        }
+                        nBits = sum;
+                    }
+                }
+
+                return nBits;
+            }
+        }
+
+        public static int BitCnt(int i)
+        {
+            uint u = (uint)i;
+            u = u - ((u >> 1) & 0x55555555);
+            u = (u & 0x33333333) + ((u >> 2) & 0x33333333);
+            u = (u + (u >> 4)) & 0x0f0f0f0f;
+            u += (u >> 8);
+            u += (u >> 16);
+            u &= 0x3f;
+            return (int)u;
+        }
+
+        private static int CalcBitLength(int sign, 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(sign, 0, magnitude);
+                }
+
+                return nBitLength;
+            }
+        }
+
+        //
+        // BitLen(value) is the number of bits in value.
+        //
+        internal static int BitLen(int w)
+        {
+            uint v = (uint)w;
+            uint t = v >> 24;
+            if (t != 0)
+                return 24 + BitLengthTable[t];
+            t = v >> 16;
+            if (t != 0)
+                return 16 + BitLengthTable[t];
+            t = v >> 8;
+            if (t != 0)
+                return 8 + BitLengthTable[t];
+            return BitLengthTable[v];
+        }
+
+        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(1, yStart, y);
+                int xBitLength = CalcBitLength(1, 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;
+
+            return sign == biggie.sign && IsEqualMagnitude(biggie);
+        }
+
+        private bool IsEqualMagnitude(BigInteger x)
+        {
+            int[] xMag = x.magnitude;
+            if (magnitude.Length != x.magnitude.Length)
+                return false;
+            for (int i = 0; i < magnitude.Length; i++)
+            {
+                if (magnitude[i] != x.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
+            {
+                if (sign == 0)
+                    return 0;
+
+                int n = magnitude.Length;
+
+                int v = magnitude[n - 1];
+
+                return sign < 0 ? -v : v;
+            }
+        }
+
+        /**
+         * return whether or not a BigInteger is probably prime with a
+         * probability of 1 - (1/2)**certainty.
+         * <p>From Knuth Vol 2, pg 395.</p>
+         */
+        public bool IsProbablePrime(int certainty)
+        {
+            return IsProbablePrime(certainty, false);
+        }
+
+        internal bool IsProbablePrime(int certainty, bool randomlySelected)
+        {
+            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, randomlySelected);
+        }
+
+        private bool CheckProbablePrime(int certainty, Random random, bool randomlySelected)
+        {
+            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, randomlySelected);
+//			return SolovayStrassenTest(certainty, random);
+
+//			bool rbTest = RabinMillerTest(certainty, random);
+//			bool ssTest = SolovayStrassenTest(certainty, random);
+//
+//			Debug.Assert(rbTest == ssTest);
+//
+//			return rbTest;
+        }
+
+        public bool RabinMillerTest(int certainty, Random random)
+        {
+            return RabinMillerTest(certainty, random, false);
+        }
+
+        internal bool RabinMillerTest(int certainty, Random random, bool randomlySelected)
+        {
+            int bits = BitLength;
+
+            Debug.Assert(certainty > 0);
+            Debug.Assert(bits > 2);
+            Debug.Assert(TestBit(0));
+
+            int iterations = ((certainty - 1) / 2) + 1;
+            if (randomlySelected)
+            {
+                int itersFor100Cert = bits >= 1024 ?  4
+                                    : bits >= 512  ?  8
+                                    : bits >= 256  ? 16
+                                    : 50;
+
+                if (certainty < 100)
+                {
+                    iterations = System.Math.Min(itersFor100Cert, iterations);
+                }
+                else
+                {
+                    iterations -= 50;
+                    iterations += itersFor100Cert;
+                }
+            }
+
+            // let n = 1 + d . 2^s
+            BigInteger n = this;
+            int s = n.GetLowestSetBitMaskFirst(-1 << 1);
+            Debug.Assert(s >= 1);
+            BigInteger r = n.ShiftRight(s);
+
+            // NOTE: Avoid conversion to/from Montgomery form and check for R/-R as result instead
+
+            BigInteger montRadix = One.ShiftLeft(32 * n.magnitude.Length).Remainder(n);
+            BigInteger minusMontRadix = n.Subtract(montRadix);
+
+            do
+            {
+                BigInteger a;
+                do
+                {
+                    a = new BigInteger(n.BitLength, random);
+                }
+                while (a.sign == 0 || a.CompareTo(n) >= 0
+                    || a.IsEqualMagnitude(montRadix) || a.IsEqualMagnitude(minusMontRadix));
+
+                BigInteger y = ModPowMonty(a, r, n, false);
+
+                if (!y.Equals(montRadix))
+                {
+                    int j = 0;
+                    while (!y.Equals(minusMontRadix))
+                    {
+                        if (++j == s)
+                            return false;
+
+                        y = ModPowMonty(y, Two, n, false);
+
+                        if (y.Equals(montRadix))
+                            return false;
+                    }
+                }
+            }
+            while (--iterations > 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;
+
+                int n = magnitude.Length;
+
+                long v = magnitude[n - 1] & IMASK;
+                if (n > 1)
+                {
+                    v |= (magnitude[n - 2] & IMASK) << 32;
+                }
+
+                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);
+//				}
+//			}
+
+            if (m.QuickPow2Check())
+            {
+                return ModInversePow2(m);
+            }
+
+            BigInteger d = this.Remainder(m);
+            BigInteger x;
+            BigInteger gcd = ExtEuclid(d, m, out x);
+
+            if (!gcd.Equals(One))
+                throw new ArithmeticException("Numbers not relatively prime.");
+
+            if (x.sign < 0)
+            {
+                x = x.Add(m);
+            }
+
+            return x;
+        }
+
+        private BigInteger ModInversePow2(BigInteger m)
+        {
+            Debug.Assert(m.SignValue > 0);
+            Debug.Assert(m.BitCount == 1);
+
+            if (!TestBit(0))
+            {
+                throw new ArithmeticException("Numbers not relatively prime.");
+            }
+
+            int pow = m.BitLength - 1;
+
+            long inv64 = ModInverse64(LongValue);
+            if (pow < 64)
+            {
+                inv64 &= ((1L << pow) - 1);
+            }
+
+            BigInteger x = BigInteger.ValueOf(inv64);
+
+            if (pow > 64)
+            {
+                BigInteger d = this.Remainder(m);
+                int bitsCorrect = 64;
+
+                do
+                {
+                    BigInteger t = x.Multiply(d).Remainder(m);
+                    x = x.Multiply(Two.Subtract(t)).Remainder(m);
+                    bitsCorrect <<= 1;
+                }
+                while (bitsCorrect < pow);
+            }
+
+            if (x.sign < 0)
+            {
+                x = x.Add(m);
+            }
+
+            return x;
+        }
+
+        private static int ModInverse32(int d)
+        {
+            // Newton's method with initial estimate "correct to 4 bits"
+            Debug.Assert((d & 1) != 0);
+            int x = d + (((d + 1) & 4) << 1);   // d.x == 1 mod 2**4
+            Debug.Assert(((d * x) & 15) == 1);
+            x *= 2 - d * x;                     // d.x == 1 mod 2**8
+            x *= 2 - d * x;                     // d.x == 1 mod 2**16
+            x *= 2 - d * x;                     // d.x == 1 mod 2**32
+            Debug.Assert(d * x == 1);
+            return x;
+        }
+
+        private static long ModInverse64(long d)
+        {
+            // Newton's method with initial estimate "correct to 4 bits"
+            Debug.Assert((d & 1L) != 0);
+            long x = d + (((d + 1L) & 4L) << 1);    // d.x == 1 mod 2**4
+            Debug.Assert(((d * x) & 15L) == 1L);
+            x *= 2 - d * x;                         // d.x == 1 mod 2**8
+            x *= 2 - d * x;                         // d.x == 1 mod 2**16
+            x *= 2 - d * x;                         // d.x == 1 mod 2**32
+            x *= 2 - d * x;                         // d.x == 1 mod 2**64
+            Debug.Assert(d * x == 1L);
+            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
+         * @return     The greatest common divisor of a and b
+         */
+        private static BigInteger ExtEuclid(BigInteger a, BigInteger b, out BigInteger u1Out)
+        {
+            BigInteger u1 = One, v1 = Zero;
+            BigInteger u3 = a, v3 = b;
+
+            if (v3.sign > 0)
+            {
+                for (;;)
+                {
+                    BigInteger[] q = u3.DivideAndRemainder(v3);
+                    u3 = v3;
+                    v3 = q[1];
+
+                    BigInteger oldU1 = u1;
+                    u1 = v1;
+
+                    if (v3.sign <= 0)
+                        break;
+
+                    v1 = oldU1.Subtract(v1.Multiply(q[0]));
+                }
+            }
+
+            u1Out = u1;
+
+            return u3;
+        }
+
+        private static void ZeroOut(
+            int[] x)
+        {
+            Array.Clear(x, 0, x.Length);
+        }
+
+        public BigInteger ModPow(BigInteger e, BigInteger m)
+        {
+            if (m.sign < 1)
+                throw new ArithmeticException("Modulus must be positive");
+
+            if (m.Equals(One))
+                return Zero;
+
+            if (e.sign == 0)
+                return One;
+
+            if (sign == 0)
+                return Zero;
+
+            bool negExp = e.sign < 0;
+            if (negExp)
+                e = e.Negate();
+
+            BigInteger result = this.Mod(m);
+            if (!e.Equals(One))
+            {
+                if ((m.magnitude[m.magnitude.Length - 1] & 1) == 0)
+                {
+                    result = ModPowBarrett(result, e, m);
+                }
+                else
+                {
+                    result = ModPowMonty(result, e, m, true);
+                }
+            }
+
+            if (negExp)
+                result = result.ModInverse(m);
+
+            return result;
+        }
+
+        private static BigInteger ModPowBarrett(BigInteger b, BigInteger e, BigInteger m)
+        {
+            int k = m.magnitude.Length;
+            BigInteger mr = One.ShiftLeft((k + 1) << 5);
+            BigInteger yu = One.ShiftLeft(k << 6).Divide(m);
+
+            // Sliding window from MSW to LSW
+            int extraBits = 0, expLength = e.BitLength;
+            while (expLength > ExpWindowThresholds[extraBits])
+            {
+                ++extraBits;
+            }
+
+            int numPowers = 1 << extraBits;
+            BigInteger[] oddPowers = new BigInteger[numPowers];
+            oddPowers[0] = b;
+
+            BigInteger b2 = ReduceBarrett(b.Square(), m, mr, yu);
+
+            for (int i = 1; i < numPowers; ++i)
+            {
+                oddPowers[i] = ReduceBarrett(oddPowers[i - 1].Multiply(b2), m, mr, yu);
+            }
+
+            int[] windowList = GetWindowList(e.magnitude, extraBits);
+            Debug.Assert(windowList.Length > 0);
+
+            int window = windowList[0];
+            int mult = window & 0xFF, lastZeroes = window >> 8;
+
+            BigInteger y;
+            if (mult == 1)
+            {
+                y = b2;
+                --lastZeroes;
+            }
+            else
+            {
+                y = oddPowers[mult >> 1];
+            }
+
+            int windowPos = 1;
+            while ((window = windowList[windowPos++]) != -1)
+            {
+                mult = window & 0xFF;
+
+                int bits = lastZeroes + BitLengthTable[mult];
+                for (int j = 0; j < bits; ++j)
+                {
+                    y = ReduceBarrett(y.Square(), m, mr, yu);
+                }
+
+                y = ReduceBarrett(y.Multiply(oddPowers[mult >> 1]), m, mr, yu);
+
+                lastZeroes = window >> 8;
+            }
+
+            for (int i = 0; i < lastZeroes; ++i)
+            {
+                y = ReduceBarrett(y.Square(), m, mr, yu);
+            }
+
+            return y;
+        }
+
+        private static BigInteger ReduceBarrett(BigInteger x, BigInteger m, BigInteger mr, BigInteger yu)
+        {
+            int xLen = x.BitLength, mLen = m.BitLength;
+            if (xLen < mLen)
+                return x;
+
+            if (xLen - mLen > 1)
+            {
+                int k = m.magnitude.Length;
+
+                BigInteger q1 = x.DivideWords(k - 1);
+                BigInteger q2 = q1.Multiply(yu); // TODO Only need partial multiplication here
+                BigInteger q3 = q2.DivideWords(k + 1);
+
+                BigInteger r1 = x.RemainderWords(k + 1);
+                BigInteger r2 = q3.Multiply(m); // TODO Only need partial multiplication here
+                BigInteger r3 = r2.RemainderWords(k + 1);
+
+                x = r1.Subtract(r3);
+                if (x.sign < 0)
+                {
+                    x = x.Add(mr);
+                }
+            }
+
+            while (x.CompareTo(m) >= 0)
+            {
+                x = x.Subtract(m);
+            }
+
+            return x;
+        }
+
+        private static BigInteger ModPowMonty(BigInteger b, BigInteger e, BigInteger m, bool convert)
+        {
+            int n = m.magnitude.Length;
+            int powR = 32 * n;
+            bool smallMontyModulus = m.BitLength + 2 <= powR;
+            uint mDash = (uint)m.GetMQuote();
+
+            // tmp = this * R mod m
+            if (convert)
+            {
+                b = b.ShiftLeft(powR).Remainder(m);
+            }
+
+            int[] yAccum = new int[n + 1];
+
+            int[] zVal = b.magnitude;
+            Debug.Assert(zVal.Length <= n);
+            if (zVal.Length < n)
+            {
+                int[] tmp = new int[n];
+                zVal.CopyTo(tmp, n - zVal.Length);
+                zVal = tmp;
+            }
+
+            // Sliding window from MSW to LSW
+
+            int extraBits = 0;
+
+            // Filter the common case of small RSA exponents with few bits set
+            if (e.magnitude.Length > 1 || e.BitCount > 2)
+            {
+                int expLength = e.BitLength;
+                while (expLength > ExpWindowThresholds[extraBits])
+                {
+                    ++extraBits;
+                }
+            }
+
+            int numPowers = 1 << extraBits;
+            int[][] oddPowers = new int[numPowers][];
+            oddPowers[0] = zVal;
+
+            int[] zSquared = Arrays.Clone(zVal);
+            SquareMonty(yAccum, zSquared, m.magnitude, mDash, smallMontyModulus);
+
+            for (int i = 1; i < numPowers; ++i)
+            {
+                oddPowers[i] = Arrays.Clone(oddPowers[i - 1]);
+                MultiplyMonty(yAccum, oddPowers[i], zSquared, m.magnitude, mDash, smallMontyModulus);
+            }
+
+            int[] windowList = GetWindowList(e.magnitude, extraBits);
+            Debug.Assert(windowList.Length > 1);
+
+            int window = windowList[0];
+            int mult = window & 0xFF, lastZeroes = window >> 8;
+
+            int[] yVal;
+            if (mult == 1)
+            {
+                yVal = zSquared;
+                --lastZeroes;
+            }
+            else
+            {
+                yVal = Arrays.Clone(oddPowers[mult >> 1]);
+            }
+
+            int windowPos = 1;
+            while ((window = windowList[windowPos++]) != -1)
+            {
+                mult = window & 0xFF;
+
+                int bits = lastZeroes + BitLengthTable[mult];
+                for (int j = 0; j < bits; ++j)
+                {
+                    SquareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus);
+                }
+
+                MultiplyMonty(yAccum, yVal, oddPowers[mult >> 1], m.magnitude, mDash, smallMontyModulus);
+
+                lastZeroes = window >> 8;
+            }
+
+            for (int i = 0; i < lastZeroes; ++i)
+            {
+                SquareMonty(yAccum, yVal, m.magnitude, mDash, smallMontyModulus);
+            }
+
+            if (convert)
+            {
+                // Return y * R^(-1) mod m
+                MontgomeryReduce(yVal, m.magnitude, mDash);
+            }
+            else if (smallMontyModulus && CompareTo(0, yVal, 0, m.magnitude) >= 0)
+            {
+                Subtract(0, yVal, 0, m.magnitude);
+            }
+
+            return new BigInteger(1, yVal, true);
+        }
+
+        private static int[] GetWindowList(int[] mag, int extraBits)
+        {
+            int v = mag[0];
+            Debug.Assert(v != 0);
+
+            int leadingBits = BitLen(v);
+
+            int resultSize = (((mag.Length - 1) << 5) + leadingBits) / (1 + extraBits) + 2;
+            int[] result = new int[resultSize];
+            int resultPos = 0;
+
+            int bitPos = 33 - leadingBits;
+            v <<= bitPos;
+
+            int mult = 1, multLimit = 1 << extraBits;
+            int zeroes = 0;
+
+            int i = 0;
+            for (; ; )
+            {
+                for (; bitPos < 32; ++bitPos)
+                {
+                    if (mult < multLimit)
+                    {
+                        mult = (mult << 1) | (int)((uint)v >> 31);
+                    }
+                    else if (v < 0)
+                    {
+                        result[resultPos++] = CreateWindowEntry(mult, zeroes);
+                        mult = 1;
+                        zeroes = 0;
+                    }
+                    else
+                    {
+                        ++zeroes;
+                    }
+
+                    v <<= 1;
+                }
+
+                if (++i == mag.Length)
+                {
+                    result[resultPos++] = CreateWindowEntry(mult, zeroes);
+                    break;
+                }
+
+                v = mag[i];
+                bitPos = 0;
+            }
+
+            result[resultPos] = -1;
+            return result;
+        }
+
+        private static int CreateWindowEntry(int mult, int zeroes)
+        {
+            while ((mult & 1) == 0)
+            {
+                mult >>= 1;
+                ++zeroes;
+            }
+
+            return mult | (zeroes << 8);
+        }
+
+        /**
+         * 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 c;
+
+            int wBase = w.Length - 1;
+
+            for (int i = x.Length - 1; i > 0; --i)
+            {
+                ulong v = (uint)x[i];
+
+                c = v * v + (uint)w[wBase];
+                w[wBase] = (int)c;
+                c >>= 32;
+
+                for (int j = i - 1; j >= 0; --j)
+                {
+                    ulong prod = v * (uint)x[j];
+
+                    c += ((uint)w[--wBase] & UIMASK) + ((uint)prod << 1);
+                    w[wBase] = (int)c;
+                    c = (c >> 32) + (prod >> 31);
+                }
+
+                c += (uint)w[--wBase];
+                w[wBase] = (int)c;
+
+                if (--wBase >= 0)
+                {
+                    w[wBase] = (int)(c >> 32);
+                }
+                else
+                {
+                    Debug.Assert((c >> 32) == 0);
+                }
+
+                wBase += i;
+            }
+
+            c = (uint)x[0];
+
+            c = c * c + (uint)w[wBase];
+            w[wBase] = (int)c;
+
+            if (--wBase >= 0)
+            {
+                w[wBase] += (int)(c >> 32);
+            }
+            else
+            {
+                Debug.Assert((c >> 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;
+
+            do
+            {
+                long a = z[--i] & IMASK;
+                long val = 0;
+
+                if (a != 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 (xBase >= 0)
+                {
+                    x[xBase] = (int)val;
+                }
+                else
+                {
+                    Debug.Assert(val == 0);
+                }
+            }
+            while (i > 0);
+
+            return x;
+        }
+
+        /**
+         * Calculate mQuote = -m^(-1) mod b with b = 2^32 (32 = word size)
+         */
+        private int GetMQuote()
+        {
+            if (mQuote != 0)
+            {
+                return mQuote; // already calculated
+            }
+
+            Debug.Assert(this.sign > 0);
+
+            int d = -magnitude[magnitude.Length - 1];
+
+            Debug.Assert((d & 1) != 0);
+
+            return mQuote = ModInverse32(d);
+        }
+
+        private static void MontgomeryReduce(int[] x, int[] m, uint mDash) // mDash = -m^(-1) mod b
+        {
+            // NOTE: Not a general purpose reduction (which would allow x up to twice the bitlength of m)
+            Debug.Assert(x.Length == m.Length);
+
+            int n = m.Length;
+
+            for (int i = n - 1; i >= 0; --i)
+            {
+                uint x0 = (uint)x[n - 1];
+                ulong t = x0 * mDash;
+
+                ulong carry = t * (uint)m[n - 1] + x0;
+                Debug.Assert((uint)carry == 0);
+                carry >>= 32;
+
+                for (int j = n - 2; j >= 0; --j)
+                {
+                    carry += t * (uint)m[j] + (uint)x[j];
+                    x[j + 1] = (int)carry;
+                    carry >>= 32;
+                }
+
+                x[0] = (int)carry;
+                Debug.Assert(carry >> 32 == 0);
+            }
+
+            if (CompareTo(0, x, 0, m) >= 0)
+            {
+                Subtract(0, x, 0, m);
+            }
+        }
+
+        /**
+         * Montgomery multiplication: a = x * y * R^(-1) mod m
+         * <br/>
+         * Based algorithm 14.36 of Handbook of Applied Cryptography.
+         * <br/>
+         * <li> m, x, y should have length n </li>
+         * <li> a should have length (n + 1) </li>
+         * <li> b = 2^32, R = b^n </li>
+         * <br/>
+         * The result is put in x
+         * <br/>
+         * 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, uint mDash, bool smallMontyModulus)
+            // mDash = -m^(-1) mod b
+        {
+            int n = m.Length;
+
+            if (n == 1)
+            {
+                x[0] = (int)MultiplyMontyNIsOne((uint)x[0], (uint)y[0], (uint)m[0], mDash);
+                return;
+            }
+
+            uint y0 = (uint)y[n - 1];
+            int aMax;
+
+            {
+                ulong xi = (uint)x[n - 1];
+
+                ulong carry = xi * y0;
+                ulong t = (uint)carry * mDash;
+
+                ulong prod2 = t * (uint)m[n - 1];
+                carry += (uint)prod2;
+                Debug.Assert((uint)carry == 0);
+                carry = (carry >> 32) + (prod2 >> 32);
+
+                for (int j = n - 2; j >= 0; --j)
+                {
+                    ulong prod1 = xi * (uint)y[j];
+                    prod2 = t * (uint)m[j];
+
+                    carry += (prod1 & UIMASK) + (uint)prod2;
+                    a[j + 2] = (int)carry;
+                    carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32);
+                }
+
+                a[1] = (int)carry;
+                aMax = (int)(carry >> 32);
+            }
+
+            for (int i = n - 2; i >= 0; --i)
+            {
+                uint a0 = (uint)a[n];
+                ulong xi = (uint)x[i];
+
+                ulong prod1 = xi * y0;
+                ulong carry = (prod1 & UIMASK) + a0;
+                ulong t = (uint)carry * mDash;
+
+                ulong prod2 = t * (uint)m[n - 1];
+                carry += (uint)prod2;
+                Debug.Assert((uint)carry == 0);
+                carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32);
+
+                for (int j = n - 2; j >= 0; --j)
+                {
+                    prod1 = xi * (uint)y[j];
+                    prod2 = t * (uint)m[j];
+
+                    carry += (prod1 & UIMASK) + (uint)prod2 + (uint)a[j + 1];
+                    a[j + 2] = (int)carry;
+                    carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32);
+                }
+
+                carry += (uint)aMax;
+                a[1] = (int)carry;
+                aMax = (int)(carry >> 32);
+            }
+
+            a[0] = aMax;
+
+            if (!smallMontyModulus && CompareTo(0, a, 0, m) >= 0)
+            {
+                Subtract(0, a, 0, m);
+            }
+
+            Array.Copy(a, 1, x, 0, n);
+        }
+
+        private static void SquareMonty(int[] a, int[] x, int[] m, uint mDash, bool smallMontyModulus)
+            // mDash = -m^(-1) mod b
+        {
+            int n = m.Length;
+
+            if (n == 1)
+            {
+                uint xVal = (uint)x[0];
+                x[0] = (int)MultiplyMontyNIsOne(xVal, xVal, (uint)m[0], mDash);
+                return;
+            }
+
+            ulong x0 = (uint)x[n - 1];
+            int aMax;
+
+            {
+                ulong carry = x0 * x0;
+                ulong t = (uint)carry * mDash;
+
+                ulong prod2 = t * (uint)m[n - 1];
+                carry += (uint)prod2;
+                Debug.Assert((uint)carry == 0);
+                carry = (carry >> 32) + (prod2 >> 32);
+
+                for (int j = n - 2; j >= 0; --j)
+                {
+                    ulong prod1 = x0 * (uint)x[j];
+                    prod2 = t * (uint)m[j];
+
+                    carry += (prod2 & UIMASK) + ((uint)prod1 << 1);
+                    a[j + 2] = (int)carry;
+                    carry = (carry >> 32) + (prod1 >> 31) + (prod2 >> 32);
+                }
+
+                a[1] = (int)carry;
+                aMax = (int)(carry >> 32);
+            }
+
+            for (int i = n - 2; i >= 0; --i)
+            {
+                uint a0 = (uint)a[n];
+                ulong t = a0 * mDash;
+
+                ulong carry = t * (uint)m[n - 1] + a0;
+                Debug.Assert((uint)carry == 0);
+                carry >>= 32;
+
+                for (int j = n - 2; j > i; --j)
+                {
+                    carry += t * (uint)m[j] + (uint)a[j + 1];
+                    a[j + 2] = (int)carry;
+                    carry >>= 32;
+                }
+
+                ulong xi = (uint)x[i];
+
+                {
+                    ulong prod1 = xi * xi;
+                    ulong prod2 = t * (uint)m[i];
+
+                    carry += (prod1 & UIMASK) + (uint)prod2 + (uint)a[i + 1];
+                    a[i + 2] = (int)carry;
+                    carry = (carry >> 32) + (prod1 >> 32) + (prod2 >> 32);
+                }
+
+                for (int j = i - 1; j >= 0; --j)
+                {
+                    ulong prod1 = xi * (uint)x[j];
+                    ulong prod2 = t * (uint)m[j];
+
+                    carry += (prod2 & UIMASK) + ((uint)prod1 << 1) + (uint)a[j + 1];
+                    a[j + 2] = (int)carry;
+                    carry = (carry >> 32) + (prod1 >> 31) + (prod2 >> 32);
+                }
+
+                carry += (uint)aMax;
+                a[1] = (int)carry;
+                aMax = (int)(carry >> 32);
+            }
+
+            a[0] = aMax;
+
+            if (!smallMontyModulus && CompareTo(0, a, 0, m) >= 0)
+            {
+                Subtract(0, a, 0, m);
+            }
+
+            Array.Copy(a, 1, x, 0, n);
+        }
+
+        private static uint MultiplyMontyNIsOne(uint x, uint y, uint m, uint mDash)
+        {
+            ulong carry = (ulong)x * y;
+            uint t = (uint)carry * mDash;
+            ulong um = m;
+            ulong prod2 = um * t;
+            carry += (uint)prod2;
+            Debug.Assert((uint)carry == 0);
+            carry = (carry >> 32) + (prod2 >> 32);
+            if (carry > um)
+            {
+                carry -= um;
+            }
+            Debug.Assert(carry < um);
+            return (uint)carry;
+        }
+
+        public BigInteger Multiply(
+            BigInteger val)
+        {
+            if (val == this)
+                return Square();
+
+            if ((sign & 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 = magnitude.Length + val.magnitude.Length;
+            int[] res = new int[resLength];
+
+            Multiply(res, this.magnitude, val.magnitude);
+
+            int resSign = sign ^ val.sign ^ 1;
+            return new BigInteger(resSign, res, true);
+        }
+
+        public BigInteger Square()
+        {
+            if (sign == 0)
+                return Zero;
+            if (this.QuickPow2Check())
+                return ShiftLeft(Abs().BitLength - 1);
+            int resLength = magnitude.Length << 1;
+            if ((uint)magnitude[0] >> 16 == 0)
+                --resLength;
+            int[] res = new int[resLength];
+            Square(res, magnitude);
+            return new BigInteger(1, res, false);
+        }
+
+        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, false))
+            {
+                n = n.Add(Two);
+            }
+
+            return n;
+        }
+
+        public BigInteger Not()
+        {
+            return Inc().Negate();
+        }
+
+        public BigInteger Pow(int exp)
+        {
+            if (exp <= 0)
+            {
+                if (exp < 0)
+                    throw new ArithmeticException("Negative exponent");
+
+                return One;
+            }
+
+            if (sign == 0)
+            {
+                return this;
+            }
+
+            if (QuickPow2Check())
+            {
+                long powOf2 = (long)exp * (BitLength - 1);
+                if (powOf2 > Int32.MaxValue)
+                {
+                    throw new ArithmeticException("Result too large");
+                }
+                return One.ShiftLeft((int)powOf2); 
+            }
+
+            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 static 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(1, yStart, y);
+                int xBitLength = CalcBitLength(1, 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 excessBits = (numWords << 5) - n;
+            if (excessBits > 0)
+            {
+                result[0] &= (int)(UInt32.MaxValue >> excessBits);
+            }
+
+            return result;
+        }
+
+        private BigInteger DivideWords(int w)
+        {
+            Debug.Assert(w >= 0);
+            int n = magnitude.Length;
+            if (w >= n)
+                return Zero;
+            int[] mag = new int[n - w];
+            Array.Copy(magnitude, 0, mag, 0, n - w);
+            return new BigInteger(sign, mag, false);
+        }
+
+        private BigInteger RemainderWords(int w)
+        {
+            Debug.Assert(w >= 0);
+            int n = magnitude.Length;
+            if (w >= n)
+                return this;
+            int[] mag = new int[w];
+            Array.Copy(magnitude, n - w, mag, 0, w);
+            return new BigInteger(sign, mag, false);
+        }
+
+        /**
+         * 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;
+        }
+
+        private static int ShiftLeftOneInPlace(int[] x, int carry)
+        {
+            Debug.Assert(carry == 0 || carry == 1);
+            int pos = x.Length;
+            while (--pos >= 0)
+            {
+                uint val = (uint)x[pos];
+                x[pos] = (int)(val << 1) | carry;
+                carry = (int)(val >> 31);
+            }
+            return carry;
+        }
+
+        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 <= 36 as in Java)
+
+            switch (radix)
+            {
+                case 2:
+                case 8:
+                case 10:
+                case 16:
+                    break;
+                default:
+                    throw new FormatException("Only bases 2, 8, 10, 16 are allowed");
+            }
+
+            // NB: Can only happen to internally managed instances
+            if (magnitude == null)
+                return "null";
+
+            if (sign == 0)
+                return "0";
+
+
+            // NOTE: This *should* be unnecessary, since the magnitude *should* never have leading zero digits
+            int firstNonZero = 0;
+            while (firstNonZero < magnitude.Length)
+            {
+                if (magnitude[firstNonZero] != 0)
+                {
+                    break;
+                }
+                ++firstNonZero;
+            }
+
+            if (firstNonZero == magnitude.Length)
+            {
+                return "0";
+            }
+
+
+            StringBuilder sb = new StringBuilder();
+            if (sign == -1)
+            {
+                sb.Append('-');
+            }
+
+            switch (radix)
+            {
+            case 2:
+            {
+                int pos = firstNonZero;
+                sb.Append(Convert.ToString(magnitude[pos], 2));
+                while (++pos < magnitude.Length)
+                {
+                    AppendZeroExtendedString(sb, Convert.ToString(magnitude[pos], 2), 32);
+                }
+                break;
+            }
+            case 8:
+            {
+                int mask = (1 << 30) - 1;
+                BigInteger u = this.Abs();
+                int bits = u.BitLength;
+                IList S = new List<object>();
+                while (bits > 30)
+                {
+                    S.Add(Convert.ToString(u.IntValue & mask, 8));
+                    u = u.ShiftRight(30);
+                    bits -= 30;
+                }
+                sb.Append(Convert.ToString(u.IntValue, 8));
+                for (int i = S.Count - 1; i >= 0; --i)
+                {
+                    AppendZeroExtendedString(sb, (string)S[i], 10);
+                }
+                break;
+            }
+            case 16:
+            {
+                int pos = firstNonZero;
+                sb.Append(Convert.ToString(magnitude[pos], 16));
+                while (++pos < magnitude.Length)
+                {
+                    AppendZeroExtendedString(sb, Convert.ToString(magnitude[pos], 16), 8);
+                }
+                break;
+            }
+            // TODO This could work for other radices if there is an alternative to Convert.ToString method
+            //default:
+            case 10:
+            {
+                BigInteger q = this.Abs();
+                if (q.BitLength < 64)
+                {
+                    sb.Append(Convert.ToString(q.LongValue, radix));
+                    break;
+                }
+
+                // TODO Could cache the moduli for each radix (soft reference?)
+                IList moduli = new List<object>();
+                BigInteger R = BigInteger.ValueOf(radix);
+                while (R.CompareTo(q) <= 0)
+                {
+                    moduli.Add(R);
+                    R = R.Square();
+                }
+
+                int scale = moduli.Count;
+                sb.EnsureCapacity(sb.Length + (1 << scale));
+
+                ToString(sb, radix, moduli, scale, q);
+
+                break;
+            }
+            }
+
+            return sb.ToString();
+        }
+
+        private static void ToString(StringBuilder sb, int radix, IList moduli, int scale, BigInteger pos)
+        {
+            if (pos.BitLength < 64)
+            {
+                string s = Convert.ToString(pos.LongValue, radix);
+                if (sb.Length > 1 || (sb.Length == 1 && sb[0] != '-'))
+                {
+                    AppendZeroExtendedString(sb, s, 1 << scale);
+                }
+                else if (pos.SignValue != 0)
+                {
+                    sb.Append(s);
+                }
+                return;
+            }
+
+            BigInteger[] qr = pos.DivideAndRemainder((BigInteger)moduli[--scale]);
+
+            ToString(sb, radix, moduli, scale, qr[0]);
+            ToString(sb, radix, moduli, scale, qr[1]);
+        }
+
+        private static void AppendZeroExtendedString(StringBuilder sb, string s, int minLength)
+        {
+            for (int len = s.Length; len < minLength; ++len)
+            {
+                sb.Append('0');
+            }
+            sb.Append(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);
+        }
+
+        public static BigInteger ValueOf(
+            long value)
+        {
+            if (value >= 0 && value < SMALL_CONSTANTS.Length)
+            {
+                return SMALL_CONSTANTS[value];
+            }
+
+            return CreateValueOf(value);
+        }
+
+        public int GetLowestSetBit()
+        {
+            if (this.sign == 0)
+                return -1;
+
+            return GetLowestSetBitMaskFirst(-1);
+        }
+
+        private int GetLowestSetBitMaskFirst(int firstWordMask)
+        {
+            int w = magnitude.Length, offset = 0;
+
+            uint word = (uint)(magnitude[--w] & firstWordMask);
+            Debug.Assert(magnitude[0] != 0);
+
+            while (word == 0)
+            {
+                word = (uint)magnitude[--w];
+                offset += 32;
+            }
+
+            while ((word & 0xFF) == 0)
+            {
+                word >>= 8;
+                offset += 8;
+            }
+
+            while ((word & 1) == 0)
+            {
+                word >>= 1;
+                ++offset;
+            }
+
+            return offset;
+        }
+
+        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);
+        }
+    }
+}

+ 496 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/ECAlgorithms.cs

@@ -0,0 +1,496 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Endo;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.Field;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC
+{
+    internal class ECAlgorithms
+    {
+        public static bool IsF2mCurve(ECCurve c)
+        {
+            return IsF2mField(c.Field);
+        }
+
+        public static bool IsF2mField(IFiniteField field)
+        {
+            return field.Dimension > 1 && field.Characteristic.Equals(BigInteger.Two)
+                && field is IPolynomialExtensionField;
+        }
+
+        public static bool IsFpCurve(ECCurve c)
+        {
+            return IsFpField(c.Field);
+        }
+
+        public static bool IsFpField(IFiniteField field)
+        {
+            return field.Dimension == 1;
+        }
+
+        public static ECPoint SumOfMultiplies(ECPoint[] ps, BigInteger[] ks)
+        {
+            if (ps == null || ks == null || ps.Length != ks.Length || ps.Length < 1)
+                throw new ArgumentException("point and scalar arrays should be non-null, and of equal, non-zero, length");
+
+            int count = ps.Length;
+            switch (count)
+            {
+                case 1:
+                    return ps[0].Multiply(ks[0]);
+                case 2:
+                    return SumOfTwoMultiplies(ps[0], ks[0], ps[1], ks[1]);
+                default:
+                    break;
+            }
+
+            ECPoint p = ps[0];
+            ECCurve c = p.Curve;
+
+            ECPoint[] imported = new ECPoint[count];
+            imported[0] = p;
+            for (int i = 1; i < count; ++i)
+            {
+                imported[i] = ImportPoint(c, ps[i]);
+            }
+
+            GlvEndomorphism glvEndomorphism = c.GetEndomorphism() as GlvEndomorphism;
+            if (glvEndomorphism != null)
+            {
+                return ImplCheckResult(ImplSumOfMultipliesGlv(imported, ks, glvEndomorphism));
+            }
+
+            return ImplCheckResult(ImplSumOfMultiplies(imported, ks));
+        }
+
+        public static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b)
+        {
+            ECCurve cp = P.Curve;
+            Q = ImportPoint(cp, Q);
+
+            // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
+            {
+                AbstractF2mCurve f2mCurve = cp as AbstractF2mCurve;
+                if (f2mCurve != null && f2mCurve.IsKoblitz)
+                {
+                    return ImplCheckResult(P.Multiply(a).Add(Q.Multiply(b)));
+                }
+            }
+
+            GlvEndomorphism glvEndomorphism = cp.GetEndomorphism() as GlvEndomorphism;
+            if (glvEndomorphism != null)
+            {
+                return ImplCheckResult(
+                    ImplSumOfMultipliesGlv(new ECPoint[] { P, Q }, new BigInteger[] { a, b }, glvEndomorphism));
+            }
+
+            return ImplCheckResult(ImplShamirsTrickWNaf(P, a, Q, b));
+        }
+
+        /*
+        * "Shamir's Trick", originally due to E. G. Straus
+        * (Addition chains of vectors. American Mathematical Monthly,
+        * 71(7):806-808, Aug./Sept. 1964)
+        *  
+        * Input: The points P, Q, scalar k = (km?, ... , k1, k0)
+        * and scalar l = (lm?, ... , l1, l0).
+        * Output: R = k * P + l * Q.
+        * 1: Z <- P + Q
+        * 2: R <- O
+        * 3: for i from m-1 down to 0 do
+        * 4:        R <- R + R        {point doubling}
+        * 5:        if (ki = 1) and (li = 0) then R <- R + P end if
+        * 6:        if (ki = 0) and (li = 1) then R <- R + Q end if
+        * 7:        if (ki = 1) and (li = 1) then R <- R + Z end if
+        * 8: end for
+        * 9: return R
+        */
+        public static ECPoint ShamirsTrick(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)
+        {
+            ECCurve cp = P.Curve;
+            Q = ImportPoint(cp, Q);
+
+            return ImplCheckResult(ImplShamirsTrickJsf(P, k, Q, l));
+        }
+
+        public static ECPoint ImportPoint(ECCurve c, ECPoint p)
+        {
+            ECCurve cp = p.Curve;
+            if (!c.Equals(cp))
+                throw new ArgumentException("Point must be on the same curve");
+
+            return c.ImportPoint(p);
+        }
+
+        public static void MontgomeryTrick(ECFieldElement[] zs, int off, int len)
+        {
+            MontgomeryTrick(zs, off, len, null);
+        }
+
+        public static void MontgomeryTrick(ECFieldElement[] zs, int off, int len, ECFieldElement scale)
+        {
+            /*
+             * Uses the "Montgomery Trick" to invert many field elements, with only a single actual
+             * field inversion. See e.g. the paper:
+             * "Fast Multi-scalar Multiplication Methods on Elliptic Curves with Precomputation Strategy Using Montgomery Trick"
+             * by Katsuyuki Okeya, Kouichi Sakurai.
+             */
+
+            ECFieldElement[] c = new ECFieldElement[len];
+            c[0] = zs[off];
+
+            int i = 0;
+            while (++i < len)
+            {
+                c[i] = c[i - 1].Multiply(zs[off + i]);
+            }
+
+            --i;
+
+            if (scale != null)
+            {
+                c[i] = c[i].Multiply(scale);
+            }
+
+            ECFieldElement u = c[i].Invert();
+
+            while (i > 0)
+            {
+                int j = off + i--;
+                ECFieldElement tmp = zs[j];
+                zs[j] = c[i].Multiply(u);
+                u = u.Multiply(tmp);
+            }
+
+            zs[off] = u;
+        }
+
+        /**
+         * Simple shift-and-add multiplication. Serves as reference implementation
+         * to verify (possibly faster) implementations, and for very small scalars.
+         * 
+         * @param p
+         *            The point to multiply.
+         * @param k
+         *            The multiplier.
+         * @return The result of the point multiplication <code>kP</code>.
+         */
+        public static ECPoint ReferenceMultiply(ECPoint p, BigInteger k)
+        {
+            BigInteger x = k.Abs();
+            ECPoint q = p.Curve.Infinity;
+            int t = x.BitLength;
+            if (t > 0)
+            {
+                if (x.TestBit(0))
+                {
+                    q = p;
+                }
+                for (int i = 1; i < t; i++)
+                {
+                    p = p.Twice();
+                    if (x.TestBit(i))
+                    {
+                        q = q.Add(p);
+                    }
+                }
+            }
+            return k.SignValue < 0 ? q.Negate() : q;
+        }
+
+        public static ECPoint ValidatePoint(ECPoint p)
+        {
+            if (!p.IsValid())
+                throw new InvalidOperationException("Invalid point");
+
+            return p;
+        }
+
+        public static ECPoint CleanPoint(ECCurve c, ECPoint p)
+        {
+            ECCurve cp = p.Curve;
+            if (!c.Equals(cp))
+                throw new ArgumentException("Point must be on the same curve", "p");
+
+            return c.DecodePoint(p.GetEncoded(false));
+        }
+
+        internal static ECPoint ImplCheckResult(ECPoint p)
+        {
+            if (!p.IsValidPartial())
+                throw new InvalidOperationException("Invalid result");
+
+            return p;
+        }
+
+        internal static ECPoint ImplShamirsTrickJsf(ECPoint P, BigInteger k, ECPoint Q, BigInteger l)
+        {
+            ECCurve curve = P.Curve;
+            ECPoint infinity = curve.Infinity;
+
+            // TODO conjugate co-Z addition (ZADDC) can return both of these
+            ECPoint PaddQ = P.Add(Q);
+            ECPoint PsubQ = P.Subtract(Q);
+
+            ECPoint[] points = new ECPoint[] { Q, PsubQ, P, PaddQ };
+            curve.NormalizeAll(points);
+
+            ECPoint[] table = new ECPoint[] {
+            points[3].Negate(), points[2].Negate(), points[1].Negate(),
+            points[0].Negate(), infinity, points[0],
+            points[1], points[2], points[3] };
+
+            byte[] jsf = WNafUtilities.GenerateJsf(k, l);
+
+            ECPoint R = infinity;
+
+            int i = jsf.Length;
+            while (--i >= 0)
+            {
+                int jsfi = jsf[i];
+
+                // NOTE: The shifting ensures the sign is extended correctly
+                int kDigit = ((jsfi << 24) >> 28), lDigit = ((jsfi << 28) >> 28);
+
+                int index = 4 + (kDigit * 3) + lDigit;
+                R = R.TwicePlus(table[index]);
+            }
+
+            return R;
+        }
+
+        internal static ECPoint ImplShamirsTrickWNaf(ECPoint P, BigInteger k,
+            ECPoint Q, BigInteger l)
+        {
+            bool negK = k.SignValue < 0, negL = l.SignValue < 0;
+
+            k = k.Abs();
+            l = l.Abs();
+
+            int widthP = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(k.BitLength)));
+            int widthQ = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(l.BitLength)));
+
+            WNafPreCompInfo infoP = WNafUtilities.Precompute(P, widthP, true);
+            WNafPreCompInfo infoQ = WNafUtilities.Precompute(Q, widthQ, true);
+
+            ECPoint[] preCompP = negK ? infoP.PreCompNeg : infoP.PreComp;
+            ECPoint[] preCompQ = negL ? infoQ.PreCompNeg : infoQ.PreComp;
+            ECPoint[] preCompNegP = negK ? infoP.PreComp : infoP.PreCompNeg;
+            ECPoint[] preCompNegQ = negL ? infoQ.PreComp : infoQ.PreCompNeg;
+
+            byte[] wnafP = WNafUtilities.GenerateWindowNaf(widthP, k);
+            byte[] wnafQ = WNafUtilities.GenerateWindowNaf(widthQ, l);
+
+            return ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ);
+        }
+
+        internal static ECPoint ImplShamirsTrickWNaf(ECPoint P, BigInteger k, ECPointMap pointMapQ, BigInteger l)
+        {
+            bool negK = k.SignValue < 0, negL = l.SignValue < 0;
+
+            k = k.Abs();
+            l = l.Abs();
+
+            int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(System.Math.Max(k.BitLength, l.BitLength))));
+
+            ECPoint Q = WNafUtilities.MapPointWithPrecomp(P, width, true, pointMapQ);
+            WNafPreCompInfo infoP = WNafUtilities.GetWNafPreCompInfo(P);
+            WNafPreCompInfo infoQ = WNafUtilities.GetWNafPreCompInfo(Q);
+
+            ECPoint[] preCompP = negK ? infoP.PreCompNeg : infoP.PreComp;
+            ECPoint[] preCompQ = negL ? infoQ.PreCompNeg : infoQ.PreComp;
+            ECPoint[] preCompNegP = negK ? infoP.PreComp : infoP.PreCompNeg;
+            ECPoint[] preCompNegQ = negL ? infoQ.PreComp : infoQ.PreCompNeg;
+
+            byte[] wnafP = WNafUtilities.GenerateWindowNaf(width, k);
+            byte[] wnafQ = WNafUtilities.GenerateWindowNaf(width, l);
+
+            return ImplShamirsTrickWNaf(preCompP, preCompNegP, wnafP, preCompQ, preCompNegQ, wnafQ);
+        }
+
+        private static ECPoint ImplShamirsTrickWNaf(ECPoint[] preCompP, ECPoint[] preCompNegP, byte[] wnafP,
+            ECPoint[] preCompQ, ECPoint[] preCompNegQ, byte[] wnafQ)
+        {
+            int len = System.Math.Max(wnafP.Length, wnafQ.Length);
+
+            ECCurve curve = preCompP[0].Curve;
+            ECPoint infinity = curve.Infinity;
+
+            ECPoint R = infinity;
+            int zeroes = 0;
+
+            for (int i = len - 1; i >= 0; --i)
+            {
+                int wiP = i < wnafP.Length ? (int)(sbyte)wnafP[i] : 0;
+                int wiQ = i < wnafQ.Length ? (int)(sbyte)wnafQ[i] : 0;
+
+                if ((wiP | wiQ) == 0)
+                {
+                    ++zeroes;
+                    continue;
+                }
+
+                ECPoint r = infinity;
+                if (wiP != 0)
+                {
+                    int nP = System.Math.Abs(wiP);
+                    ECPoint[] tableP = wiP < 0 ? preCompNegP : preCompP;
+                    r = r.Add(tableP[nP >> 1]);
+                }
+                if (wiQ != 0)
+                {
+                    int nQ = System.Math.Abs(wiQ);
+                    ECPoint[] tableQ = wiQ < 0 ? preCompNegQ : preCompQ;
+                    r = r.Add(tableQ[nQ >> 1]);
+                }
+
+                if (zeroes > 0)
+                {
+                    R = R.TimesPow2(zeroes);
+                    zeroes = 0;
+                }
+
+                R = R.TwicePlus(r);
+            }
+
+            if (zeroes > 0)
+            {
+                R = R.TimesPow2(zeroes);
+            }
+
+            return R;
+        }
+
+        internal static ECPoint ImplSumOfMultiplies(ECPoint[] ps, BigInteger[] ks)
+        {
+            int count = ps.Length;
+            bool[] negs = new bool[count];
+            WNafPreCompInfo[] infos = new WNafPreCompInfo[count];
+            byte[][] wnafs = new byte[count][];
+
+            for (int i = 0; i < count; ++i)
+            {
+                BigInteger ki = ks[i]; negs[i] = ki.SignValue < 0; ki = ki.Abs();
+
+                int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(ki.BitLength)));
+                infos[i] = WNafUtilities.Precompute(ps[i], width, true);
+                wnafs[i] = WNafUtilities.GenerateWindowNaf(width, ki);
+            }
+
+            return ImplSumOfMultiplies(negs, infos, wnafs);
+        }
+
+        internal static ECPoint ImplSumOfMultipliesGlv(ECPoint[] ps, BigInteger[] ks, GlvEndomorphism glvEndomorphism)
+        {
+            BigInteger n = ps[0].Curve.Order;
+
+            int len = ps.Length;
+
+            BigInteger[] abs = new BigInteger[len << 1];
+            for (int i = 0, j = 0; i < len; ++i)
+            {
+                BigInteger[] ab = glvEndomorphism.DecomposeScalar(ks[i].Mod(n));
+                abs[j++] = ab[0];
+                abs[j++] = ab[1];
+            }
+
+            ECPointMap pointMap = glvEndomorphism.PointMap;
+            if (glvEndomorphism.HasEfficientPointMap)
+            {
+                return ECAlgorithms.ImplSumOfMultiplies(ps, pointMap, abs);
+            }
+
+            ECPoint[] pqs = new ECPoint[len << 1];
+            for (int i = 0, j = 0; i < len; ++i)
+            {
+                ECPoint p = ps[i], q = pointMap.Map(p);
+                pqs[j++] = p;
+                pqs[j++] = q;
+            }
+
+            return ECAlgorithms.ImplSumOfMultiplies(pqs, abs);
+        }
+
+        internal static ECPoint ImplSumOfMultiplies(ECPoint[] ps, ECPointMap pointMap, BigInteger[] ks)
+        {
+            int halfCount = ps.Length, fullCount = halfCount << 1;
+
+            bool[] negs = new bool[fullCount];
+            WNafPreCompInfo[] infos = new WNafPreCompInfo[fullCount];
+            byte[][] wnafs = new byte[fullCount][];
+
+            for (int i = 0; i < halfCount; ++i)
+            {
+                int j0 = i << 1, j1 = j0 + 1;
+
+                BigInteger kj0 = ks[j0]; negs[j0] = kj0.SignValue < 0; kj0 = kj0.Abs();
+                BigInteger kj1 = ks[j1]; negs[j1] = kj1.SignValue < 0; kj1 = kj1.Abs();
+
+                int width = System.Math.Max(2, System.Math.Min(16, WNafUtilities.GetWindowSize(System.Math.Max(kj0.BitLength, kj1.BitLength))));
+
+                ECPoint P = ps[i], Q = WNafUtilities.MapPointWithPrecomp(P, width, true, pointMap);
+                infos[j0] = WNafUtilities.GetWNafPreCompInfo(P);
+                infos[j1] = WNafUtilities.GetWNafPreCompInfo(Q);
+                wnafs[j0] = WNafUtilities.GenerateWindowNaf(width, kj0);
+                wnafs[j1] = WNafUtilities.GenerateWindowNaf(width, kj1);
+            }
+
+            return ImplSumOfMultiplies(negs, infos, wnafs);
+        }
+
+        private static ECPoint ImplSumOfMultiplies(bool[] negs, WNafPreCompInfo[] infos, byte[][] wnafs)
+        {
+            int len = 0, count = wnafs.Length;
+            for (int i = 0; i < count; ++i)
+            {
+                len = System.Math.Max(len, wnafs[i].Length);
+            }
+
+            ECCurve curve = infos[0].PreComp[0].Curve;
+            ECPoint infinity = curve.Infinity;
+
+            ECPoint R = infinity;
+            int zeroes = 0;
+
+            for (int i = len - 1; i >= 0; --i)
+            {
+                ECPoint r = infinity;
+
+                for (int j = 0; j < count; ++j)
+                {
+                    byte[] wnaf = wnafs[j];
+                    int wi = i < wnaf.Length ? (int)(sbyte)wnaf[i] : 0;
+                    if (wi != 0)
+                    {
+                        int n = System.Math.Abs(wi);
+                        WNafPreCompInfo info = infos[j];
+                        ECPoint[] table = (wi < 0 == negs[j]) ? info.PreComp : info.PreCompNeg;
+                        r = r.Add(table[n >> 1]);
+                    }
+                }
+
+                if (r == infinity)
+                {
+                    ++zeroes;
+                    continue;
+                }
+
+                if (zeroes > 0)
+                {
+                    R = R.TimesPow2(zeroes);
+                    zeroes = 0;
+                }
+
+                R = R.TwicePlus(r);
+            }
+
+            if (zeroes > 0)
+            {
+                R = R.TimesPow2(zeroes);
+            }
+
+            return R;
+        }
+    }
+}

+ 1278 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/ECCurve.cs

@@ -0,0 +1,1278 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Abc;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Endo;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.Field;
+using Renci.SshNet.Security.Org.BouncyCastle.Math.Raw;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC
+{
+    /// <remarks>Base class for an elliptic curve.</remarks>
+    internal abstract class ECCurve
+    {
+        public const int COORD_AFFINE = 0;
+        public const int COORD_HOMOGENEOUS = 1;
+        public const int COORD_JACOBIAN = 2;
+        public const int COORD_JACOBIAN_CHUDNOVSKY = 3;
+        public const int COORD_JACOBIAN_MODIFIED = 4;
+        public const int COORD_LAMBDA_AFFINE = 5;
+        public const int COORD_LAMBDA_PROJECTIVE = 6;
+        public const int COORD_SKEWED = 7;
+
+        public static int[] GetAllCoordinateSystems()
+        {
+            return new int[]{ COORD_AFFINE, COORD_HOMOGENEOUS, COORD_JACOBIAN, COORD_JACOBIAN_CHUDNOVSKY,
+                COORD_JACOBIAN_MODIFIED, COORD_LAMBDA_AFFINE, COORD_LAMBDA_PROJECTIVE, COORD_SKEWED };
+        }
+
+        internal class Config
+        {
+            protected ECCurve outer;
+            protected int coord;
+            protected ECEndomorphism endomorphism;
+            protected ECMultiplier multiplier;
+
+            internal Config(ECCurve outer, int coord, ECEndomorphism endomorphism, ECMultiplier multiplier)
+            {
+                this.outer = outer;
+                this.coord = coord;
+                this.endomorphism = endomorphism;
+                this.multiplier = multiplier;
+            }
+
+            public Config SetCoordinateSystem(int coord)
+            {
+                this.coord = coord;
+                return this;
+            }
+
+            public Config SetEndomorphism(ECEndomorphism endomorphism)
+            {
+                this.endomorphism = endomorphism;
+                return this;
+            }
+
+            public Config SetMultiplier(ECMultiplier multiplier)
+            {
+                this.multiplier = multiplier;
+                return this;
+            }
+
+            public ECCurve Create()
+            {
+                if (!outer.SupportsCoordinateSystem(coord))
+                {
+                    throw new InvalidOperationException("unsupported coordinate system");
+                }
+
+                ECCurve c = outer.CloneCurve();
+                if (c == outer)
+                {
+                    throw new InvalidOperationException("implementation returned current curve");
+                }
+
+                c.m_coord = coord;
+                c.m_endomorphism = endomorphism;
+                c.m_multiplier = multiplier;
+
+                return c;
+            }
+        }
+
+        protected readonly IFiniteField m_field;
+        protected ECFieldElement m_a, m_b;
+        protected BigInteger m_order, m_cofactor;
+
+        protected int m_coord = COORD_AFFINE;
+        protected ECEndomorphism m_endomorphism = null;
+        protected ECMultiplier m_multiplier = null;
+
+        protected ECCurve(IFiniteField field)
+        {
+            this.m_field = field;
+        }
+
+        public abstract int FieldSize { get; }
+        public abstract ECFieldElement FromBigInteger(BigInteger x);
+        public abstract bool IsValidFieldElement(BigInteger x);
+
+        public virtual Config Configure()
+        {
+            return new Config(this, this.m_coord, this.m_endomorphism, this.m_multiplier);
+        }
+
+        public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y)
+        {
+            ECPoint p = CreatePoint(x, y);
+            if (!p.IsValid())
+            {
+                throw new ArgumentException("Invalid point coordinates");
+            }
+            return p;
+        }
+
+        public virtual ECPoint ValidatePoint(BigInteger x, BigInteger y, bool withCompression)
+        {
+            ECPoint p = CreatePoint(x, y, withCompression);
+            if (!p.IsValid())
+            {
+                throw new ArgumentException("Invalid point coordinates");
+            }
+            return p;
+        }
+
+        public virtual ECPoint CreatePoint(BigInteger x, BigInteger y)
+        {
+            return CreatePoint(x, y, false);
+        }
+
+        public virtual ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression)
+        {
+            return CreateRawPoint(FromBigInteger(x), FromBigInteger(y), withCompression);
+        }
+
+        protected abstract ECCurve CloneCurve();
+
+        protected internal abstract ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression);
+
+        protected internal abstract ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression);
+
+        protected virtual ECMultiplier CreateDefaultMultiplier()
+        {
+            GlvEndomorphism glvEndomorphism = m_endomorphism as GlvEndomorphism;
+            if (glvEndomorphism != null)
+            {
+                return new GlvMultiplier(this, glvEndomorphism);
+            }
+
+            return new WNafL2RMultiplier();
+        }
+
+        public virtual bool SupportsCoordinateSystem(int coord)
+        {
+            return coord == COORD_AFFINE;
+        }
+
+        public virtual PreCompInfo GetPreCompInfo(ECPoint point, string name)
+        {
+            CheckPoint(point);
+
+            IDictionary table;
+            lock (point)
+            {
+                table = point.m_preCompTable;
+            }
+
+            if (null == table)
+                return null;
+
+            lock (table)
+            {
+                return (PreCompInfo)table[name];
+            }
+        }
+
+        /**
+         * Compute a <code>PreCompInfo</code> for a point on this curve, under a given name. Used by
+         * <code>ECMultiplier</code>s to save the precomputation for this <code>ECPoint</code> for use
+         * by subsequent multiplication.
+         * 
+         * @param point
+         *            The <code>ECPoint</code> to store precomputations for.
+         * @param name
+         *            A <code>String</code> used to index precomputations of different types.
+         * @param callback
+         *            Called to calculate the <code>PreCompInfo</code>.
+         */
+        public virtual PreCompInfo Precompute(ECPoint point, string name, IPreCompCallback callback)
+        {
+            CheckPoint(point);
+
+            IDictionary table;
+            lock (point)
+            {
+                table = point.m_preCompTable;
+                if (null == table)
+                {
+                    point.m_preCompTable = table = new Dictionary<object, object>(4);
+                }
+            }
+
+            lock (table)
+            {
+                PreCompInfo existing = (PreCompInfo)table[name];
+                PreCompInfo result = callback.Precompute(existing);
+
+                if (result != existing)
+                {
+                    table[name] = result;
+                }
+
+                return result;
+            }
+        }
+
+        public virtual ECPoint ImportPoint(ECPoint p)
+        {
+            if (this == p.Curve)
+            {
+                return p;
+            }
+            if (p.IsInfinity)
+            {
+                return Infinity;
+            }
+
+            // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
+            p = p.Normalize();
+
+            return CreatePoint(p.XCoord.ToBigInteger(), p.YCoord.ToBigInteger(), p.IsCompressed);
+        }
+
+        /**
+         * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+         * coordinates reflect those of the equivalent point in an affine coordinate system. Where more
+         * than one point is to be normalized, this method will generally be more efficient than
+         * normalizing each point separately.
+         * 
+         * @param points
+         *            An array of points that will be updated in place with their normalized versions,
+         *            where necessary
+         */
+        public virtual void NormalizeAll(ECPoint[] points)
+        {
+            NormalizeAll(points, 0, points.Length, null);
+        }
+
+        /**
+         * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+         * coordinates reflect those of the equivalent point in an affine coordinate system. Where more
+         * than one point is to be normalized, this method will generally be more efficient than
+         * normalizing each point separately. An (optional) z-scaling factor can be applied; effectively
+         * each z coordinate is scaled by this value prior to normalization (but only one
+         * actual multiplication is needed).
+         * 
+         * @param points
+         *            An array of points that will be updated in place with their normalized versions,
+         *            where necessary
+         * @param off
+         *            The start of the range of points to normalize
+         * @param len
+         *            The length of the range of points to normalize
+         * @param iso
+         *            The (optional) z-scaling factor - can be null
+         */
+        public virtual void NormalizeAll(ECPoint[] points, int off, int len, ECFieldElement iso)
+        {
+            CheckPoints(points, off, len);
+
+            switch (this.CoordinateSystem)
+            {
+                case ECCurve.COORD_AFFINE:
+                case ECCurve.COORD_LAMBDA_AFFINE:
+                {
+                    if (iso != null)
+                        throw new ArgumentException("not valid for affine coordinates", "iso");
+
+                    return;
+                }
+            }
+
+            /*
+             * Figure out which of the points actually need to be normalized
+             */
+            ECFieldElement[] zs = new ECFieldElement[len];
+            int[] indices = new int[len];
+            int count = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint p = points[off + i];
+                if (null != p && (iso != null || !p.IsNormalized()))
+                {
+                    zs[count] = p.GetZCoord(0);
+                    indices[count++] = off + i;
+                }
+            }
+
+            if (count == 0)
+            {
+                return;
+            }
+
+            ECAlgorithms.MontgomeryTrick(zs, 0, count, iso);
+
+            for (int j = 0; j < count; ++j)
+            {
+                int index = indices[j];
+                points[index] = points[index].Normalize(zs[j]);
+            }
+        }
+
+        public abstract ECPoint Infinity { get; }
+
+        public virtual IFiniteField Field
+        {
+            get { return m_field; }
+        }
+
+        public virtual ECFieldElement A
+        {
+            get { return m_a; }
+        }
+
+        public virtual ECFieldElement B
+        {
+            get { return m_b; }
+        }
+
+        public virtual BigInteger Order
+        {
+            get { return m_order; }
+        }
+
+        public virtual BigInteger Cofactor
+        {
+            get { return m_cofactor; }
+        }
+
+        public virtual int CoordinateSystem
+        {
+            get { return m_coord; }
+        }
+
+        /**
+         * Create a cache-safe lookup table for the specified sequence of points. All the points MUST
+         * belong to this <code>ECCurve</code> instance, and MUST already be normalized.
+         */
+        public virtual ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            int FE_BYTES = (FieldSize + 7) / 8;
+            byte[] table = new byte[len * FE_BYTES * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    byte[] px = p.RawXCoord.ToBigInteger().ToByteArray();
+                    byte[] py = p.RawYCoord.ToBigInteger().ToByteArray();
+
+                    int pxStart = px.Length > FE_BYTES ? 1 : 0, pxLen = px.Length - pxStart;
+                    int pyStart = py.Length > FE_BYTES ? 1 : 0, pyLen = py.Length - pyStart;
+
+                    Array.Copy(px, pxStart, table, pos + FE_BYTES - pxLen, pxLen); pos += FE_BYTES;
+                    Array.Copy(py, pyStart, table, pos + FE_BYTES - pyLen, pyLen); pos += FE_BYTES;
+                }
+            }
+
+            return new DefaultLookupTable(this, table, len);
+        }
+
+        protected virtual void CheckPoint(ECPoint point)
+        {
+            if (null == point || (this != point.Curve))
+                throw new ArgumentException("must be non-null and on this curve", "point");
+        }
+
+        protected virtual void CheckPoints(ECPoint[] points)
+        {
+            CheckPoints(points, 0, points.Length);
+        }
+
+        protected virtual void CheckPoints(ECPoint[] points, int off, int len)
+        {
+            if (points == null)
+                throw new ArgumentNullException("points");
+            if (off < 0 || len < 0 || (off > (points.Length - len)))
+                throw new ArgumentException("invalid range specified", "points");
+
+            for (int i = 0; i < len; ++i)
+            {
+                ECPoint point = points[off + i];
+                if (null != point && this != point.Curve)
+                    throw new ArgumentException("entries must be null or on this curve", "points");
+            }
+        }
+
+        public virtual bool Equals(ECCurve other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return Field.Equals(other.Field)
+                && A.ToBigInteger().Equals(other.A.ToBigInteger())
+                && B.ToBigInteger().Equals(other.B.ToBigInteger());
+        }
+
+        public override bool Equals(object obj) 
+        {
+            return Equals(obj as ECCurve);
+        }
+
+        public override int GetHashCode()
+        {
+            return Field.GetHashCode()
+                ^ Integers.RotateLeft(A.ToBigInteger().GetHashCode(), 8)
+                ^ Integers.RotateLeft(B.ToBigInteger().GetHashCode(), 16);
+        }
+
+        protected abstract ECPoint DecompressPoint(int yTilde, BigInteger X1);
+
+        public virtual ECEndomorphism GetEndomorphism()
+        {
+            return m_endomorphism;
+        }
+
+        /**
+         * Sets the default <code>ECMultiplier</code>, unless already set. 
+         */
+        public virtual ECMultiplier GetMultiplier()
+        {
+            lock (this)
+            {
+                if (this.m_multiplier == null)
+                {
+                    this.m_multiplier = CreateDefaultMultiplier();
+                }
+                return this.m_multiplier;
+            }
+        }
+
+        /**
+         * Decode a point on this curve from its ASN.1 encoding. The different
+         * encodings are taken account of, including point compression for
+         * <code>F<sub>p</sub></code> (X9.62 s 4.2.1 pg 17).
+         * @return The decoded point.
+         */
+        public virtual ECPoint DecodePoint(byte[] encoded)
+        {
+            ECPoint p = null;
+            int expectedLength = (FieldSize + 7) / 8;
+
+            byte type = encoded[0];
+            switch (type)
+            {
+                case 0x00: // infinity
+                {
+                    if (encoded.Length != 1)
+                        throw new ArgumentException("Incorrect length for infinity encoding", "encoded");
+
+                    p = Infinity;
+                    break;
+                }
+
+                case 0x02: // compressed
+                case 0x03: // compressed
+                {
+                    if (encoded.Length != (expectedLength + 1))
+                        throw new ArgumentException("Incorrect length for compressed encoding", "encoded");
+
+                    int yTilde = type & 1;
+                    BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
+
+                    p = DecompressPoint(yTilde, X);
+                    if (!p.ImplIsValid(true, true))
+                        throw new ArgumentException("Invalid point");
+
+                    break;
+                }
+
+                case 0x04: // uncompressed
+                {
+                    if (encoded.Length != (2 * expectedLength + 1))
+                        throw new ArgumentException("Incorrect length for uncompressed encoding", "encoded");
+
+                    BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
+                    BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength);
+
+                    p = ValidatePoint(X, Y);
+                    break;
+                }
+
+                case 0x06: // hybrid
+                case 0x07: // hybrid
+                {
+                    if (encoded.Length != (2 * expectedLength + 1))
+                        throw new ArgumentException("Incorrect length for hybrid encoding", "encoded");
+
+                    BigInteger X = new BigInteger(1, encoded, 1, expectedLength);
+                    BigInteger Y = new BigInteger(1, encoded, 1 + expectedLength, expectedLength);
+
+                    if (Y.TestBit(0) != (type == 0x07))
+                        throw new ArgumentException("Inconsistent Y coordinate in hybrid encoding", "encoded");
+
+                    p = ValidatePoint(X, Y);
+                    break;
+                }
+
+                default:
+                    throw new FormatException("Invalid point encoding " + type);
+            }
+
+            if (type != 0x00 && p.IsInfinity)
+                throw new ArgumentException("Invalid infinity encoding", "encoded");
+
+            return p;
+        }
+
+        private class DefaultLookupTable
+            : ECLookupTable
+        {
+            private readonly ECCurve m_outer;
+            private readonly byte[] m_table;
+            private readonly int m_size;
+
+            internal DefaultLookupTable(ECCurve outer, byte[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                int FE_BYTES = (m_outer.FieldSize + 7) / 8;
+                byte[] x = new byte[FE_BYTES], y = new byte[FE_BYTES];
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    byte MASK = (byte)(((i ^ index) - 1) >> 31);
+
+                    for (int j = 0; j < FE_BYTES; ++j)
+                    {
+                        x[j] ^= (byte)(m_table[pos + j] & MASK);
+                        y[j] ^= (byte)(m_table[pos + FE_BYTES + j] & MASK);
+                    }
+
+                    pos += (FE_BYTES * 2);
+                }
+
+                ECFieldElement X = m_outer.FromBigInteger(new BigInteger(1, x));
+                ECFieldElement Y = m_outer.FromBigInteger(new BigInteger(1, y));
+                return m_outer.CreateRawPoint(X, Y, false);
+            }
+        }
+    }
+
+    internal abstract class AbstractFpCurve
+        : ECCurve
+    {
+        protected AbstractFpCurve(BigInteger q)
+            : base(FiniteFields.GetPrimeField(q))
+        {
+        }
+
+        public override bool IsValidFieldElement(BigInteger x)
+        {
+            return x != null && x.SignValue >= 0 && x.CompareTo(Field.Characteristic) < 0;
+        }
+
+        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
+        {
+            ECFieldElement x = FromBigInteger(X1);
+            ECFieldElement rhs = x.Square().Add(A).Multiply(x).Add(B);
+            ECFieldElement y = rhs.Sqrt();
+
+            /*
+             * If y is not a square, then we haven't got a point on the curve
+             */
+            if (y == null)
+                throw new ArgumentException("Invalid point compression");
+
+            if (y.TestBitZero() != (yTilde == 1))
+            {
+                // Use the other root
+                y = y.Negate();
+            }
+
+            return CreateRawPoint(x, y, true);
+        }
+    }
+
+    /**
+     * Elliptic curve over Fp
+     */
+    internal class FpCurve
+        : AbstractFpCurve
+    {
+        private const int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+
+        protected readonly BigInteger m_q, m_r;
+        protected readonly FpPoint m_infinity;
+
+        public FpCurve(BigInteger q, BigInteger a, BigInteger b)
+            : this(q, a, b, null, null)
+        {
+        }
+
+        public FpCurve(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger cofactor)
+            : base(q)
+        {
+            this.m_q = q;
+            this.m_r = FpFieldElement.CalculateResidue(q);
+            this.m_infinity = new FpPoint(this, null, null, false);
+
+            this.m_a = FromBigInteger(a);
+            this.m_b = FromBigInteger(b);
+            this.m_order = order;
+            this.m_cofactor = cofactor;
+            this.m_coord = FP_DEFAULT_COORDS;
+        }
+
+        protected FpCurve(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b)
+            : this(q, r, a, b, null, null)
+        {
+        }
+
+        protected FpCurve(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor)
+            : base(q)
+        {
+            this.m_q = q;
+            this.m_r = r;
+            this.m_infinity = new FpPoint(this, null, null, false);
+
+            this.m_a = a;
+            this.m_b = b;
+            this.m_order = order;
+            this.m_cofactor = cofactor;
+            this.m_coord = FP_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new FpCurve(m_q, m_r, m_a, m_b, m_order, m_cofactor);
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+                case COORD_AFFINE:
+                case COORD_HOMOGENEOUS:
+                case COORD_JACOBIAN:
+                case COORD_JACOBIAN_MODIFIED:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        public virtual BigInteger Q
+        {
+            get { return m_q; }
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public override int FieldSize
+        {
+            get { return m_q.BitLength; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new FpFieldElement(this.m_q, this.m_r, x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new FpPoint(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new FpPoint(this, x, y, zs, withCompression);
+        }
+
+        public override ECPoint ImportPoint(ECPoint p)
+        {
+            if (this != p.Curve && this.CoordinateSystem == COORD_JACOBIAN && !p.IsInfinity)
+            {
+                switch (p.Curve.CoordinateSystem)
+                {
+                    case COORD_JACOBIAN:
+                    case COORD_JACOBIAN_CHUDNOVSKY:
+                    case COORD_JACOBIAN_MODIFIED:
+                        return new FpPoint(this,
+                            FromBigInteger(p.RawXCoord.ToBigInteger()),
+                            FromBigInteger(p.RawYCoord.ToBigInteger()),
+                            new ECFieldElement[] { FromBigInteger(p.GetZCoord(0).ToBigInteger()) },
+                            p.IsCompressed);
+                    default:
+                        break;
+                }
+            }
+
+            return base.ImportPoint(p);
+        }
+    }
+
+    internal abstract class AbstractF2mCurve
+        : ECCurve
+    {
+        public static BigInteger Inverse(int m, int[] ks, BigInteger x)
+        {
+            return new LongArray(x).ModInverse(m, ks).ToBigInteger();
+        }
+
+        /**
+         * The auxiliary values <code>s<sub>0</sub></code> and
+         * <code>s<sub>1</sub></code> used for partial modular reduction for
+         * Koblitz curves.
+         */
+        private BigInteger[] si = null;
+
+        private static IFiniteField BuildField(int m, int k1, int k2, int k3)
+        {
+            if (k1 == 0)
+            {
+                throw new ArgumentException("k1 must be > 0");
+            }
+
+            if (k2 == 0)
+            {
+                if (k3 != 0)
+                {
+                    throw new ArgumentException("k3 must be 0 if k2 == 0");
+                }
+
+                return FiniteFields.GetBinaryExtensionField(new int[]{ 0, k1, m });
+            }
+
+            if (k2 <= k1)
+            {
+                throw new ArgumentException("k2 must be > k1");
+            }
+
+            if (k3 <= k2)
+            {
+                throw new ArgumentException("k3 must be > k2");
+            }
+
+            return FiniteFields.GetBinaryExtensionField(new int[]{ 0, k1, k2, k3, m });
+        }
+
+        protected AbstractF2mCurve(int m, int k1, int k2, int k3)
+            : base(BuildField(m, k1, k2, k3))
+        {
+        }
+
+        public override bool IsValidFieldElement(BigInteger x)
+        {
+            return x != null && x.SignValue >= 0 && x.BitLength <= FieldSize;
+        }
+
+        public override ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression)
+        {
+            ECFieldElement X = FromBigInteger(x), Y = FromBigInteger(y);
+
+            switch (this.CoordinateSystem)
+            {
+                case COORD_LAMBDA_AFFINE:
+                case COORD_LAMBDA_PROJECTIVE:
+                {
+                    if (X.IsZero)
+                    {
+                        if (!Y.Square().Equals(B))
+                            throw new ArgumentException();
+                    }
+                    else
+                    {
+                        // Y becomes Lambda (X + Y/X) here
+                        Y = Y.Divide(X).Add(X);
+                    }
+                    break;
+                }
+                default:
+                {
+                    break;
+                }
+            }
+
+            return CreateRawPoint(X, Y, withCompression);
+        }
+
+        protected override ECPoint DecompressPoint(int yTilde, BigInteger X1)
+        {
+            ECFieldElement xp = FromBigInteger(X1), yp = null;
+            if (xp.IsZero)
+            {
+                yp = B.Sqrt();
+            }
+            else
+            {
+                ECFieldElement beta = xp.Square().Invert().Multiply(B).Add(A).Add(xp);
+                ECFieldElement z = SolveQuadraticEquation(beta);
+
+                if (z != null)
+                {
+                    if (z.TestBitZero() != (yTilde == 1))
+                    {
+                        z = z.AddOne();
+                    }
+
+                    switch (this.CoordinateSystem)
+                    {
+                        case COORD_LAMBDA_AFFINE:
+                        case COORD_LAMBDA_PROJECTIVE:
+                        {
+                            yp = z.Add(xp);
+                            break;
+                        }
+                        default:
+                        {
+                            yp = z.Multiply(xp);
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (yp == null)
+                throw new ArgumentException("Invalid point compression");
+
+            return CreateRawPoint(xp, yp, true);
+        }
+
+        /**
+         * Solves a quadratic equation <code>z<sup>2</sup> + z = beta</code>(X9.62
+         * D.1.6) The other solution is <code>z + 1</code>.
+         *
+         * @param beta
+         *            The value to solve the quadratic equation for.
+         * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
+         *         <code>null</code> if no solution exists.
+         */
+        internal ECFieldElement SolveQuadraticEquation(ECFieldElement beta)
+        {
+            if (beta.IsZero)
+                return beta;
+
+            ECFieldElement gamma, z, zeroElement = FromBigInteger(BigInteger.Zero);
+
+            int m = FieldSize;
+            do
+            {
+                ECFieldElement t = FromBigInteger(BigInteger.Arbitrary(m));
+                z = zeroElement;
+                ECFieldElement w = beta;
+                for (int i = 1; i < m; i++)
+                {
+                    ECFieldElement w2 = w.Square();
+                    z = z.Square().Add(w2.Multiply(t));
+                    w = w2.Add(beta);
+                }
+                if (!w.IsZero)
+                {
+                    return null;
+                }
+                gamma = z.Square().Add(z);
+            }
+            while (gamma.IsZero);
+
+            return z;
+        }
+
+        /**
+         * @return the auxiliary values <code>s<sub>0</sub></code> and
+         * <code>s<sub>1</sub></code> used for partial modular reduction for
+         * Koblitz curves.
+         */
+        internal virtual BigInteger[] GetSi()
+        {
+            if (si == null)
+            {
+                lock (this)
+                {
+                    if (si == null)
+                    {
+                        si = Tnaf.GetSi(this);
+                    }
+                }
+            }
+            return si;
+        }
+
+        /**
+         * Returns true if this is a Koblitz curve (ABC curve).
+         * @return true if this is a Koblitz curve (ABC curve), false otherwise
+         */
+        public virtual bool IsKoblitz
+        {
+            get
+            {
+                return m_order != null && m_cofactor != null && m_b.IsOne && (m_a.IsZero || m_a.IsOne);
+            }
+        }
+    }
+
+    /**
+     * Elliptic curves over F2m. The Weierstrass equation is given by
+     * <code>y<sup>2</sup> + xy = x<sup>3</sup> + ax<sup>2</sup> + b</code>.
+     */
+    internal class F2mCurve
+        : AbstractF2mCurve
+    {
+        private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE;
+
+        /**
+         * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
+         */
+        private readonly int m;
+
+        /**
+         * TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+         * x<sup>k</sup> + 1</code> represents the reduction polynomial
+         * <code>f(z)</code>.<br/>
+         * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.<br/>
+         */
+        private readonly int k1;
+
+        /**
+         * TPB: Always set to <code>0</code><br/>
+         * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.<br/>
+         */
+        private readonly int k2;
+
+        /**
+         * TPB: Always set to <code>0</code><br/>
+         * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.<br/>
+         */
+        private readonly int k3;
+
+        /**
+         * The point at infinity on this curve.
+         */
+        protected readonly F2mPoint m_infinity;
+
+        /**
+         * Constructor for Trinomial Polynomial Basis (TPB).
+         * @param m  The exponent <code>m</code> of
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+         * x<sup>k</sup> + 1</code> represents the reduction
+         * polynomial <code>f(z)</code>.
+         * @param a The coefficient <code>a</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param b The coefficient <code>b</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         */
+        [Obsolete("Use constructor taking order/cofactor")]
+        public F2mCurve(
+            int			m,
+            int			k,
+            BigInteger	a,
+            BigInteger	b)
+            : this(m, k, 0, 0, a, b, null, null)
+        {
+        }
+
+        /**
+         * Constructor for Trinomial Polynomial Basis (TPB).
+         * @param m  The exponent <code>m</code> of
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+         * x<sup>k</sup> + 1</code> represents the reduction
+         * polynomial <code>f(z)</code>.
+         * @param a The coefficient <code>a</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param b The coefficient <code>b</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param order The order of the main subgroup of the elliptic curve.
+         * @param cofactor The cofactor of the elliptic curve, i.e.
+         * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
+         */
+        public F2mCurve(
+            int			m, 
+            int			k, 
+            BigInteger	a, 
+            BigInteger	b,
+            BigInteger	order,
+            BigInteger	cofactor)
+            : this(m, k, 0, 0, a, b, order, cofactor)
+        {
+        }
+
+        /**
+         * Constructor for Pentanomial Polynomial Basis (PPB).
+         * @param m  The exponent <code>m</code> of
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param a The coefficient <code>a</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param b The coefficient <code>b</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         */
+        [Obsolete("Use constructor taking order/cofactor")]
+        public F2mCurve(
+            int			m,
+            int			k1,
+            int			k2,
+            int			k3,
+            BigInteger	a,
+            BigInteger	b)
+            : this(m, k1, k2, k3, a, b, null, null)
+        {
+        }
+
+        /**
+         * Constructor for Pentanomial Polynomial Basis (PPB).
+         * @param m  The exponent <code>m</code> of
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param a The coefficient <code>a</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param b The coefficient <code>b</code> in the Weierstrass equation
+         * for non-supersingular elliptic curves over
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param order The order of the main subgroup of the elliptic curve.
+         * @param cofactor The cofactor of the elliptic curve, i.e.
+         * <code>#E<sub>a</sub>(F<sub>2<sup>m</sup></sub>) = h * n</code>.
+         */
+        public F2mCurve(
+            int			m, 
+            int			k1, 
+            int			k2, 
+            int			k3,
+            BigInteger	a, 
+            BigInteger	b,
+            BigInteger	order,
+            BigInteger	cofactor)
+            : base(m, k1, k2, k3)
+        {
+            this.m = m;
+            this.k1 = k1;
+            this.k2 = k2;
+            this.k3 = k3;
+            this.m_order = order;
+            this.m_cofactor = cofactor;
+            this.m_infinity = new F2mPoint(this, null, null, false);
+
+            if (k1 == 0)
+                throw new ArgumentException("k1 must be > 0");
+
+            if (k2 == 0)
+            {
+                if (k3 != 0)
+                    throw new ArgumentException("k3 must be 0 if k2 == 0");
+            }
+            else
+            {
+                if (k2 <= k1)
+                    throw new ArgumentException("k2 must be > k1");
+
+                if (k3 <= k2)
+                    throw new ArgumentException("k3 must be > k2");
+            }
+
+            this.m_a = FromBigInteger(a);
+            this.m_b = FromBigInteger(b);
+            this.m_coord = F2M_DEFAULT_COORDS;
+        }
+
+        protected F2mCurve(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger order, BigInteger cofactor)
+            : base(m, k1, k2, k3)
+        {
+            this.m = m;
+            this.k1 = k1;
+            this.k2 = k2;
+            this.k3 = k3;
+            this.m_order = order;
+            this.m_cofactor = cofactor;
+
+            this.m_infinity = new F2mPoint(this, null, null, false);
+            this.m_a = a;
+            this.m_b = b;
+            this.m_coord = F2M_DEFAULT_COORDS;
+        }
+
+        protected override ECCurve CloneCurve()
+        {
+            return new F2mCurve(m, k1, k2, k3, m_a, m_b, m_order, m_cofactor);
+        }
+
+        public override bool SupportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+                case COORD_AFFINE:
+                case COORD_HOMOGENEOUS:
+                case COORD_LAMBDA_PROJECTIVE:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        protected override ECMultiplier CreateDefaultMultiplier()
+        {
+            if (IsKoblitz)
+            {
+                return new WTauNafMultiplier();
+            }
+
+            return base.CreateDefaultMultiplier();
+        }
+
+        public override int FieldSize
+        {
+            get { return m; }
+        }
+
+        public override ECFieldElement FromBigInteger(BigInteger x)
+        {
+            return new F2mFieldElement(this.m, this.k1, this.k2, this.k3, x);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, bool withCompression)
+        {
+            return new F2mPoint(this, x, y, withCompression);
+        }
+
+        protected internal override ECPoint CreateRawPoint(ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            return new F2mPoint(this, x, y, zs, withCompression);
+        }
+
+        public override ECPoint Infinity
+        {
+            get { return m_infinity; }
+        }
+
+        public int M
+        {
+            get { return m; }
+        }
+
+        /**
+         * Return true if curve uses a Trinomial basis.
+         *
+         * @return true if curve Trinomial, false otherwise.
+         */
+        public bool IsTrinomial()
+        {
+            return k2 == 0 && k3 == 0;
+        }
+
+        public int K1
+        {
+            get { return k1; }
+        }
+
+        public int K2
+        {
+            get { return k2; }
+        }
+
+        public int K3
+        {
+            get { return k3; }
+        }
+
+        public override ECLookupTable CreateCacheSafeLookupTable(ECPoint[] points, int off, int len)
+        {
+            int FE_LONGS = (m + 63) / 64;
+
+            long[] table = new long[len * FE_LONGS * 2];
+            {
+                int pos = 0;
+                for (int i = 0; i < len; ++i)
+                {
+                    ECPoint p = points[off + i];
+                    ((F2mFieldElement)p.RawXCoord).x.CopyTo(table, pos); pos += FE_LONGS;
+                    ((F2mFieldElement)p.RawYCoord).x.CopyTo(table, pos); pos += FE_LONGS;
+                }
+            }
+
+            return new DefaultF2mLookupTable(this, table, len);
+        }
+
+        private class DefaultF2mLookupTable
+            : ECLookupTable
+        {
+            private readonly F2mCurve m_outer;
+            private readonly long[] m_table;
+            private readonly int m_size;
+
+            internal DefaultF2mLookupTable(F2mCurve outer, long[] table, int size)
+            {
+                this.m_outer = outer;
+                this.m_table = table;
+                this.m_size = size;
+            }
+
+            public virtual int Size
+            {
+                get { return m_size; }
+            }
+
+            public virtual ECPoint Lookup(int index)
+            {
+                int m = m_outer.m;
+                int[] ks = m_outer.IsTrinomial() ? new int[]{ m_outer.k1 } : new int[]{ m_outer.k1, m_outer.k2, m_outer.k3 }; 
+
+                int FE_LONGS = (m_outer.m + 63) / 64;
+                long[] x = new long[FE_LONGS], y = new long[FE_LONGS];
+                int pos = 0;
+
+                for (int i = 0; i < m_size; ++i)
+                {
+                    long MASK =((i ^ index) - 1) >> 31;
+
+                    for (int j = 0; j < FE_LONGS; ++j)
+                    {
+                        x[j] ^= m_table[pos + j] & MASK;
+                        y[j] ^= m_table[pos + FE_LONGS + j] & MASK;
+                    }
+
+                    pos += (FE_LONGS * 2);
+                }
+
+                ECFieldElement X = new F2mFieldElement(m, ks, new LongArray(x));
+                ECFieldElement Y = new F2mFieldElement(m, ks, new LongArray(y));
+                return m_outer.CreateRawPoint(X, Y, false);
+            }
+        }
+    }
+}

+ 972 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/ECFieldElement.cs

@@ -0,0 +1,972 @@
+using System;
+using System.Diagnostics;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.Raw;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC
+{
+    internal abstract class ECFieldElement
+    {
+        public abstract BigInteger ToBigInteger();
+        public abstract string FieldName { get; }
+        public abstract int FieldSize { get; }
+        public abstract ECFieldElement Add(ECFieldElement b);
+        public abstract ECFieldElement AddOne();
+        public abstract ECFieldElement Subtract(ECFieldElement b);
+        public abstract ECFieldElement Multiply(ECFieldElement b);
+        public abstract ECFieldElement Divide(ECFieldElement b);
+        public abstract ECFieldElement Negate();
+        public abstract ECFieldElement Square();
+        public abstract ECFieldElement Invert();
+        public abstract ECFieldElement Sqrt();
+
+        public virtual int BitLength
+        {
+            get { return ToBigInteger().BitLength; }
+        }
+
+        public virtual bool IsOne
+        {
+            get { return BitLength == 1; }
+        }
+
+        public virtual bool IsZero
+        {
+            get { return 0 == ToBigInteger().SignValue; }
+        }
+
+        public virtual ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+        {
+            return Multiply(b).Subtract(x.Multiply(y));
+        }
+
+        public virtual ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+        {
+            return Multiply(b).Add(x.Multiply(y));
+        }
+
+        public virtual ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y)
+        {
+            return Square().Subtract(x.Multiply(y));
+        }
+
+        public virtual ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y)
+        {
+            return Square().Add(x.Multiply(y));
+        }
+
+        public virtual ECFieldElement SquarePow(int pow)
+        {
+            ECFieldElement r = this;
+            for (int i = 0; i < pow; ++i)
+            {
+                r = r.Square();
+            }
+            return r;
+        }
+
+        public virtual bool TestBitZero()
+        {
+            return ToBigInteger().TestBit(0);
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as ECFieldElement);
+        }
+
+        public virtual bool Equals(ECFieldElement other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            return ToBigInteger().Equals(other.ToBigInteger());
+        }
+
+        public override int GetHashCode()
+        {
+            return ToBigInteger().GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return this.ToBigInteger().ToString(16);
+        }
+
+        public virtual byte[] GetEncoded()
+        {
+            return BigIntegers.AsUnsignedByteArray((FieldSize + 7) / 8, ToBigInteger());
+        }
+    }
+
+    internal abstract class AbstractFpFieldElement
+        : ECFieldElement
+    {
+    }
+
+    internal class FpFieldElement
+        : AbstractFpFieldElement
+    {
+        private readonly BigInteger q, r, x;
+
+        internal static BigInteger CalculateResidue(BigInteger p)
+        {
+            int bitLength = p.BitLength;
+            if (bitLength >= 96)
+            {
+                BigInteger firstWord = p.ShiftRight(bitLength - 64);
+                if (firstWord.LongValue == -1L)
+                {
+                    return BigInteger.One.ShiftLeft(bitLength).Subtract(p);
+                }
+                if ((bitLength & 7) == 0)
+                {
+                    return BigInteger.One.ShiftLeft(bitLength << 1).Divide(p).Negate();
+                }
+            }
+            return null;
+        }
+
+        [Obsolete("Use ECCurve.FromBigInteger to construct field elements")]
+        public FpFieldElement(BigInteger q, BigInteger x)
+            : this(q, CalculateResidue(q), x)
+        {
+        }
+
+        internal FpFieldElement(BigInteger q, BigInteger r, BigInteger x)
+        {
+            if (x == null || x.SignValue < 0 || x.CompareTo(q) >= 0)
+                throw new ArgumentException("value invalid in Fp field element", "x");
+
+            this.q = q;
+            this.r = r;
+            this.x = x;
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return x;
+        }
+
+        /**
+         * return the field name for this field.
+         *
+         * @return the string "Fp".
+         */
+        public override string FieldName
+        {
+            get { return "Fp"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return q.BitLength; }
+        }
+
+        public BigInteger Q
+        {
+            get { return q; }
+        }
+
+        public override ECFieldElement Add(
+            ECFieldElement b)
+        {
+            return new FpFieldElement(q, r, ModAdd(x, b.ToBigInteger()));
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            BigInteger x2 = x.Add(BigInteger.One);
+            if (x2.CompareTo(q) == 0)
+            {
+                x2 = BigInteger.Zero;
+            }
+            return new FpFieldElement(q, r, x2);
+        }
+
+        public override ECFieldElement Subtract(
+            ECFieldElement b)
+        {
+            return new FpFieldElement(q, r, ModSubtract(x, b.ToBigInteger()));
+        }
+
+        public override ECFieldElement Multiply(
+            ECFieldElement b)
+        {
+            return new FpFieldElement(q, r, ModMult(x, b.ToBigInteger()));
+        }
+
+        public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+        {
+            BigInteger ax = this.x, bx = b.ToBigInteger(), xx = x.ToBigInteger(), yx = y.ToBigInteger();
+            BigInteger ab = ax.Multiply(bx);
+            BigInteger xy = xx.Multiply(yx);
+            return new FpFieldElement(q, r, ModReduce(ab.Subtract(xy)));
+        }
+
+        public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+        {
+            BigInteger ax = this.x, bx = b.ToBigInteger(), xx = x.ToBigInteger(), yx = y.ToBigInteger();
+            BigInteger ab = ax.Multiply(bx);
+            BigInteger xy = xx.Multiply(yx);
+            BigInteger sum = ab.Add(xy);
+            if (r != null && r.SignValue < 0 && sum.BitLength > (q.BitLength << 1))
+            {
+                sum = sum.Subtract(q.ShiftLeft(q.BitLength));
+            }
+            return new FpFieldElement(q, r, ModReduce(sum));
+        }
+
+        public override ECFieldElement Divide(
+            ECFieldElement b)
+        {
+            return new FpFieldElement(q, r, ModMult(x, ModInverse(b.ToBigInteger())));
+        }
+
+        public override ECFieldElement Negate()
+        {
+            return x.SignValue == 0 ? this : new FpFieldElement(q, r, q.Subtract(x));
+        }
+
+        public override ECFieldElement Square()
+        {
+            return new FpFieldElement(q, r, ModMult(x, x));
+        }
+
+        public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y)
+        {
+            BigInteger ax = this.x, xx = x.ToBigInteger(), yx = y.ToBigInteger();
+            BigInteger aa = ax.Multiply(ax);
+            BigInteger xy = xx.Multiply(yx);
+            return new FpFieldElement(q, r, ModReduce(aa.Subtract(xy)));
+        }
+
+        public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y)
+        {
+            BigInteger ax = this.x, xx = x.ToBigInteger(), yx = y.ToBigInteger();
+            BigInteger aa = ax.Multiply(ax);
+            BigInteger xy = xx.Multiply(yx);
+            BigInteger sum = aa.Add(xy);
+            if (r != null && r.SignValue < 0 && sum.BitLength > (q.BitLength << 1))
+            {
+                sum = sum.Subtract(q.ShiftLeft(q.BitLength));
+            }
+            return new FpFieldElement(q, r, ModReduce(sum));
+        }
+
+        public override ECFieldElement Invert()
+        {
+            // TODO Modular inversion can be faster for a (Generalized) Mersenne Prime.
+            return new FpFieldElement(q, r, ModInverse(x));
+        }
+
+        /**
+         * return a sqrt root - the routine verifies that the calculation
+         * returns the right value - if none exists it returns null.
+         */
+        public override ECFieldElement Sqrt()
+        {
+            if (IsZero || IsOne)
+                return this;
+
+            if (!q.TestBit(0))
+                throw new NotImplementedException("even value of q");
+
+            if (q.TestBit(1)) // q == 4m + 3
+            {
+                BigInteger e = q.ShiftRight(2).Add(BigInteger.One);
+                return CheckSqrt(new FpFieldElement(q, r, x.ModPow(e, q)));
+            }
+
+            if (q.TestBit(2)) // q == 8m + 5
+            {
+                BigInteger t1 = x.ModPow(q.ShiftRight(3), q);
+                BigInteger t2 = ModMult(t1, x);
+                BigInteger t3 = ModMult(t2, t1);
+
+                if (t3.Equals(BigInteger.One))
+                {
+                    return CheckSqrt(new FpFieldElement(q, r, t2));
+                }
+
+                // TODO This is constant and could be precomputed
+                BigInteger t4 = BigInteger.Two.ModPow(q.ShiftRight(2), q);
+
+                BigInteger y = ModMult(t2, t4);
+
+                return CheckSqrt(new FpFieldElement(q, r, y));
+            }
+
+            // q == 8m + 1
+
+            BigInteger legendreExponent = q.ShiftRight(1);
+            if (!(x.ModPow(legendreExponent, q).Equals(BigInteger.One)))
+                return null;
+
+            BigInteger X = this.x;
+            BigInteger fourX = ModDouble(ModDouble(X)); ;
+
+            BigInteger k = legendreExponent.Add(BigInteger.One), qMinusOne = q.Subtract(BigInteger.One);
+
+            BigInteger U, V;
+            do
+            {
+                BigInteger P;
+                do
+                {
+                    P = BigInteger.Arbitrary(q.BitLength);
+                }
+                while (P.CompareTo(q) >= 0
+                    || !ModReduce(P.Multiply(P).Subtract(fourX)).ModPow(legendreExponent, q).Equals(qMinusOne));
+
+                BigInteger[] result = LucasSequence(P, X, k);
+                U = result[0];
+                V = result[1];
+
+                if (ModMult(V, V).Equals(fourX))
+                {
+                    return new FpFieldElement(q, r, ModHalfAbs(V));
+                }
+            }
+            while (U.Equals(BigInteger.One) || U.Equals(qMinusOne));
+
+            return null;
+        }
+
+        private ECFieldElement CheckSqrt(ECFieldElement z)
+        {
+            return z.Square().Equals(this) ? z : null;
+        }
+
+        private BigInteger[] LucasSequence(
+            BigInteger	P,
+            BigInteger	Q,
+            BigInteger	k)
+        {
+            // TODO Research and apply "common-multiplicand multiplication here"
+
+            int n = k.BitLength;
+            int s = k.GetLowestSetBit();
+
+            Debug.Assert(k.TestBit(s));
+
+            BigInteger Uh = BigInteger.One;
+            BigInteger Vl = BigInteger.Two;
+            BigInteger Vh = P;
+            BigInteger Ql = BigInteger.One;
+            BigInteger Qh = BigInteger.One;
+
+            for (int j = n - 1; j >= s + 1; --j)
+            {
+                Ql = ModMult(Ql, Qh);
+
+                if (k.TestBit(j))
+                {
+                    Qh = ModMult(Ql, Q);
+                    Uh = ModMult(Uh, Vh);
+                    Vl = ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql)));
+                    Vh = ModReduce(Vh.Multiply(Vh).Subtract(Qh.ShiftLeft(1)));
+                }
+                else
+                {
+                    Qh = Ql;
+                    Uh = ModReduce(Uh.Multiply(Vl).Subtract(Ql));
+                    Vh = ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql)));
+                    Vl = ModReduce(Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)));
+                }
+            }
+
+            Ql = ModMult(Ql, Qh);
+            Qh = ModMult(Ql, Q);
+            Uh = ModReduce(Uh.Multiply(Vl).Subtract(Ql));
+            Vl = ModReduce(Vh.Multiply(Vl).Subtract(P.Multiply(Ql)));
+            Ql = ModMult(Ql, Qh);
+
+            for (int j = 1; j <= s; ++j)
+            {
+                Uh = ModMult(Uh, Vl);
+                Vl = ModReduce(Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)));
+                Ql = ModMult(Ql, Ql);
+            }
+
+            return new BigInteger[] { Uh, Vl };
+        }
+
+        protected virtual BigInteger ModAdd(BigInteger x1, BigInteger x2)
+        {
+            BigInteger x3 = x1.Add(x2);
+            if (x3.CompareTo(q) >= 0)
+            {
+                x3 = x3.Subtract(q);
+            }
+            return x3;
+        }
+
+        protected virtual BigInteger ModDouble(BigInteger x)
+        {
+            BigInteger _2x = x.ShiftLeft(1);
+            if (_2x.CompareTo(q) >= 0)
+            {
+                _2x = _2x.Subtract(q);
+            }
+            return _2x;
+        }
+
+        protected virtual BigInteger ModHalf(BigInteger x)
+        {
+            if (x.TestBit(0))
+            {
+                x = q.Add(x);
+            }
+            return x.ShiftRight(1);
+        }
+
+        protected virtual BigInteger ModHalfAbs(BigInteger x)
+        {
+            if (x.TestBit(0))
+            {
+                x = q.Subtract(x);
+            }
+            return x.ShiftRight(1);
+        }
+
+        protected virtual BigInteger ModInverse(BigInteger x)
+        {
+            int bits = FieldSize;
+            int len = (bits + 31) >> 5;
+            uint[] p = Nat.FromBigInteger(bits, q);
+            uint[] n = Nat.FromBigInteger(bits, x);
+            uint[] z = Nat.Create(len);
+            Mod.Invert(p, n, z);
+            return Nat.ToBigInteger(len, z);
+        }
+
+        protected virtual BigInteger ModMult(BigInteger x1, BigInteger x2)
+        {
+            return ModReduce(x1.Multiply(x2));
+        }
+
+        protected virtual BigInteger ModReduce(BigInteger x)
+        {
+            if (r == null)
+            {
+                x = x.Mod(q);
+            }
+            else
+            {
+                bool negative = x.SignValue < 0;
+                if (negative)
+                {
+                    x = x.Abs();
+                }
+                int qLen = q.BitLength;
+                if (r.SignValue > 0)
+                {
+                    BigInteger qMod = BigInteger.One.ShiftLeft(qLen);
+                    bool rIsOne = r.Equals(BigInteger.One);
+                    while (x.BitLength > (qLen + 1))
+                    {
+                        BigInteger u = x.ShiftRight(qLen);
+                        BigInteger v = x.Remainder(qMod);
+                        if (!rIsOne)
+                        {
+                            u = u.Multiply(r);
+                        }
+                        x = u.Add(v);
+                    }
+                }
+                else
+                {
+                    int d = ((qLen - 1) & 31) + 1;
+                    BigInteger mu = r.Negate();
+                    BigInteger u = mu.Multiply(x.ShiftRight(qLen - d));
+                    BigInteger quot = u.ShiftRight(qLen + d);
+                    BigInteger v = quot.Multiply(q);
+                    BigInteger bk1 = BigInteger.One.ShiftLeft(qLen + d);
+                    v = v.Remainder(bk1);
+                    x = x.Remainder(bk1);
+                    x = x.Subtract(v);
+                    if (x.SignValue < 0)
+                    {
+                        x = x.Add(bk1);
+                    }
+                }
+                while (x.CompareTo(q) >= 0)
+                {
+                    x = x.Subtract(q);
+                }
+                if (negative && x.SignValue != 0)
+                {
+                    x = q.Subtract(x);
+                }
+            }
+            return x;
+        }
+
+        protected virtual BigInteger ModSubtract(BigInteger x1, BigInteger x2)
+        {
+            BigInteger x3 = x1.Subtract(x2);
+            if (x3.SignValue < 0)
+            {
+                x3 = x3.Add(q);
+            }
+            return x3;
+        }
+
+        public override bool Equals(
+            object obj)
+        {
+            if (obj == this)
+                return true;
+
+            FpFieldElement other = obj as FpFieldElement;
+
+            if (other == null)
+                return false;
+
+            return Equals(other);
+        }
+
+        public virtual bool Equals(
+            FpFieldElement other)
+        {
+            return q.Equals(other.q) && base.Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            return q.GetHashCode() ^ base.GetHashCode();
+        }
+    }
+
+    internal abstract class AbstractF2mFieldElement
+        :   ECFieldElement
+    {
+        public virtual ECFieldElement HalfTrace()
+        {
+            int m = FieldSize;
+            if ((m & 1) == 0)
+                throw new InvalidOperationException("Half-trace only defined for odd m");
+
+            ECFieldElement fe = this;
+            ECFieldElement ht = fe;
+            for (int i = 2; i < m; i += 2)
+            {
+                fe = fe.SquarePow(2);
+                ht = ht.Add(fe);
+            }
+
+            return ht;
+        }
+
+        public virtual int Trace()
+        {
+            int m = FieldSize;
+            ECFieldElement fe = this;
+            ECFieldElement tr = fe;
+            for (int i = 1; i < m; ++i)
+            {
+                fe = fe.Square();
+                tr = tr.Add(fe);
+            }
+            if (tr.IsZero)
+                return 0;
+            if (tr.IsOne)
+                return 1;
+
+            throw new InvalidOperationException("Internal error in trace calculation");
+        }
+    }
+
+    /**
+     * Class representing the Elements of the finite field
+     * <code>F<sub>2<sup>m</sup></sub></code> in polynomial basis (PB)
+     * representation. Both trinomial (Tpb) and pentanomial (Ppb) polynomial
+     * basis representations are supported. Gaussian normal basis (GNB)
+     * representation is not supported.
+     */
+    internal class F2mFieldElement
+        :   AbstractF2mFieldElement
+    {
+        /**
+         * Indicates gaussian normal basis representation (GNB). Number chosen
+         * according to X9.62. GNB is not implemented at present.
+         */
+        public const int Gnb = 1;
+
+        /**
+         * Indicates trinomial basis representation (Tpb). Number chosen
+         * according to X9.62.
+         */
+        public const int Tpb = 2;
+
+        /**
+         * Indicates pentanomial basis representation (Ppb). Number chosen
+         * according to X9.62.
+         */
+        public const int Ppb = 3;
+
+        /**
+         * Tpb or Ppb.
+         */
+        private int representation;
+
+        /**
+         * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
+         */
+        private int m;
+
+        private int[] ks;
+
+        /**
+         * The <code>LongArray</code> holding the bits.
+         */
+        internal LongArray x;
+
+        /**
+         * Constructor for Ppb.
+         * @param m  The exponent <code>m</code> of
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param k1 The integer <code>k1</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param k2 The integer <code>k2</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param k3 The integer <code>k3</code> where <code>x<sup>m</sup> +
+         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+         * represents the reduction polynomial <code>f(z)</code>.
+         * @param x The BigInteger representing the value of the field element.
+         */
+        public F2mFieldElement(
+            int			m,
+            int			k1,
+            int			k2,
+            int			k3,
+            BigInteger	x)
+        {
+            if (x == null || x.SignValue < 0 || x.BitLength > m)
+                throw new ArgumentException("value invalid in F2m field element", "x");
+
+            if ((k2 == 0) && (k3 == 0))
+            {
+                this.representation = Tpb;
+                this.ks = new int[] { k1 };
+            }
+            else
+            {
+                if (k2 >= k3)
+                    throw new ArgumentException("k2 must be smaller than k3");
+                if (k2 <= 0)
+                    throw new ArgumentException("k2 must be larger than 0");
+
+                this.representation = Ppb;
+                this.ks = new int[] { k1, k2, k3 };
+            }
+
+            this.m = m;
+            this.x = new LongArray(x);
+        }
+
+        /**
+         * Constructor for Tpb.
+         * @param m  The exponent <code>m</code> of
+         * <code>F<sub>2<sup>m</sup></sub></code>.
+         * @param k The integer <code>k</code> where <code>x<sup>m</sup> +
+         * x<sup>k</sup> + 1</code> represents the reduction
+         * polynomial <code>f(z)</code>.
+         * @param x The BigInteger representing the value of the field element.
+         */
+        public F2mFieldElement(
+            int			m,
+            int			k,
+            BigInteger	x)
+            : this(m, k, 0, 0, x)
+        {
+            // Set k1 to k, and set k2 and k3 to 0
+        }
+
+        internal F2mFieldElement(int m, int[] ks, LongArray x)
+        {
+            this.m = m;
+            this.representation = (ks.Length == 1) ? Tpb : Ppb;
+            this.ks = ks;
+            this.x = x;
+        }
+
+        public override int BitLength
+        {
+            get { return x.Degree(); }
+        }
+
+        public override bool IsOne
+        {
+            get { return x.IsOne(); }
+        }
+
+        public override bool IsZero
+        {
+            get { return x.IsZero(); }
+        }
+
+        public override bool TestBitZero()
+        {
+            return x.TestBitZero();
+        }
+
+        public override BigInteger ToBigInteger()
+        {
+            return x.ToBigInteger();
+        }
+
+        public override string FieldName
+        {
+            get { return "F2m"; }
+        }
+
+        public override int FieldSize
+        {
+            get { return m; }
+        }
+
+        /**
+        * Checks, if the ECFieldElements <code>a</code> and <code>b</code>
+        * are elements of the same field <code>F<sub>2<sup>m</sup></sub></code>
+        * (having the same representation).
+        * @param a field element.
+        * @param b field element to be compared.
+        * @throws ArgumentException if <code>a</code> and <code>b</code>
+        * are not elements of the same field
+        * <code>F<sub>2<sup>m</sup></sub></code> (having the same
+        * representation).
+        */
+        public static void CheckFieldElements(
+            ECFieldElement	a,
+            ECFieldElement	b)
+        {
+            if (!(a is F2mFieldElement) || !(b is F2mFieldElement))
+            {
+                throw new ArgumentException("Field elements are not "
+                    + "both instances of F2mFieldElement");
+            }
+
+            F2mFieldElement aF2m = (F2mFieldElement)a;
+            F2mFieldElement bF2m = (F2mFieldElement)b;
+
+            if (aF2m.representation != bF2m.representation)
+            {
+                // Should never occur
+                throw new ArgumentException("One of the F2m field elements has incorrect representation");
+            }
+
+            if ((aF2m.m != bF2m.m) || !Arrays.AreEqual(aF2m.ks, bF2m.ks))
+            {
+                throw new ArgumentException("Field elements are not elements of the same field F2m");
+            }
+        }
+
+        public override ECFieldElement Add(
+            ECFieldElement b)
+        {
+            // No check performed here for performance reasons. Instead the
+            // elements involved are checked in ECPoint.F2m
+            // checkFieldElements(this, b);
+            LongArray iarrClone = this.x.Copy();
+            F2mFieldElement bF2m = (F2mFieldElement)b;
+            iarrClone.AddShiftedByWords(bF2m.x, 0);
+            return new F2mFieldElement(m, ks, iarrClone);
+        }
+
+        public override ECFieldElement AddOne()
+        {
+            return new F2mFieldElement(m, ks, x.AddOne());
+        }
+
+        public override ECFieldElement Subtract(
+            ECFieldElement b)
+        {
+            // Addition and subtraction are the same in F2m
+            return Add(b);
+        }
+
+        public override ECFieldElement Multiply(
+            ECFieldElement b)
+        {
+            // Right-to-left comb multiplication in the LongArray
+            // Input: Binary polynomials a(z) and b(z) of degree at most m-1
+            // Output: c(z) = a(z) * b(z) mod f(z)
+
+            // No check performed here for performance reasons. Instead the
+            // elements involved are checked in ECPoint.F2m
+            // checkFieldElements(this, b);
+            return new F2mFieldElement(m, ks, x.ModMultiply(((F2mFieldElement)b).x, m, ks));
+        }
+
+        public override ECFieldElement MultiplyMinusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+        {
+            return MultiplyPlusProduct(b, x, y);
+        }
+
+        public override ECFieldElement MultiplyPlusProduct(ECFieldElement b, ECFieldElement x, ECFieldElement y)
+        {
+            LongArray ax = this.x, bx = ((F2mFieldElement)b).x, xx = ((F2mFieldElement)x).x, yx = ((F2mFieldElement)y).x;
+
+            LongArray ab = ax.Multiply(bx, m, ks);
+            LongArray xy = xx.Multiply(yx, m, ks);
+
+            if (ab == ax || ab == bx)
+            {
+                ab = (LongArray)ab.Copy();
+            }
+
+            ab.AddShiftedByWords(xy, 0);
+            ab.Reduce(m, ks);
+
+            return new F2mFieldElement(m, ks, ab);
+        }
+
+        public override ECFieldElement Divide(
+            ECFieldElement b)
+        {
+            // There may be more efficient implementations
+            ECFieldElement bInv = b.Invert();
+            return Multiply(bInv);
+        }
+
+        public override ECFieldElement Negate()
+        {
+            // -x == x holds for all x in F2m
+            return this;
+        }
+
+        public override ECFieldElement Square()
+        {
+            return new F2mFieldElement(m, ks, x.ModSquare(m, ks));
+        }
+
+        public override ECFieldElement SquareMinusProduct(ECFieldElement x, ECFieldElement y)
+        {
+            return SquarePlusProduct(x, y);
+        }
+
+        public override ECFieldElement SquarePlusProduct(ECFieldElement x, ECFieldElement y)
+        {
+            LongArray ax = this.x, xx = ((F2mFieldElement)x).x, yx = ((F2mFieldElement)y).x;
+
+            LongArray aa = ax.Square(m, ks);
+            LongArray xy = xx.Multiply(yx, m, ks);
+
+            if (aa == ax)
+            {
+                aa = (LongArray)aa.Copy();
+            }
+
+            aa.AddShiftedByWords(xy, 0);
+            aa.Reduce(m, ks);
+
+            return new F2mFieldElement(m, ks, aa);
+        }
+
+        public override ECFieldElement SquarePow(int pow)
+        {
+            return pow < 1 ? this : new F2mFieldElement(m, ks, x.ModSquareN(pow, m, ks));
+        }
+
+        public override ECFieldElement Invert()
+        {
+            return new F2mFieldElement(this.m, this.ks, this.x.ModInverse(m, ks));
+        }
+
+        public override ECFieldElement Sqrt()
+        {
+            return (x.IsZero() || x.IsOne()) ? this : SquarePow(m - 1);
+        }
+
+        /**
+            * @return the representation of the field
+            * <code>F<sub>2<sup>m</sup></sub></code>, either of
+            * {@link F2mFieldElement.Tpb} (trinomial
+            * basis representation) or
+            * {@link F2mFieldElement.Ppb} (pentanomial
+            * basis representation).
+            */
+        public int Representation
+        {
+            get { return this.representation; }
+        }
+
+        /**
+            * @return the degree <code>m</code> of the reduction polynomial
+            * <code>f(z)</code>.
+            */
+        public int M
+        {
+            get { return this.m; }
+        }
+
+        /**
+            * @return Tpb: The integer <code>k</code> where <code>x<sup>m</sup> +
+            * x<sup>k</sup> + 1</code> represents the reduction polynomial
+            * <code>f(z)</code>.<br/>
+            * Ppb: The integer <code>k1</code> where <code>x<sup>m</sup> +
+            * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+            * represents the reduction polynomial <code>f(z)</code>.<br/>
+            */
+        public int K1
+        {
+            get { return this.ks[0]; }
+        }
+
+        /**
+            * @return Tpb: Always returns <code>0</code><br/>
+            * Ppb: The integer <code>k2</code> where <code>x<sup>m</sup> +
+            * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+            * represents the reduction polynomial <code>f(z)</code>.<br/>
+            */
+        public int K2
+        {
+            get { return this.ks.Length >= 2 ? this.ks[1] : 0; }
+        }
+
+        /**
+            * @return Tpb: Always set to <code>0</code><br/>
+            * Ppb: The integer <code>k3</code> where <code>x<sup>m</sup> +
+            * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+            * represents the reduction polynomial <code>f(z)</code>.<br/>
+            */
+        public int K3
+        {
+            get { return this.ks.Length >= 3 ? this.ks[2] : 0; }
+        }
+
+        public override bool Equals(
+            object obj)
+        {
+            if (obj == this)
+                return true;
+
+            F2mFieldElement other = obj as F2mFieldElement;
+
+            if (other == null)
+                return false;
+
+            return Equals(other);
+        }
+
+        public virtual bool Equals(
+            F2mFieldElement other)
+        {
+            return ((this.m == other.m)
+                && (this.representation == other.representation)
+                && Arrays.AreEqual(this.ks, other.ks)
+                && (this.x.Equals(other.x)));
+        }
+
+        public override int GetHashCode()
+        {
+            return x.GetHashCode() ^ m ^ Arrays.GetHashCode(ks);
+        }
+    }
+}

+ 10 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/ECLookupTable.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC
+{
+    internal interface ECLookupTable
+    {
+        int Size { get; }
+        ECPoint Lookup(int index);
+    }
+}

+ 2122 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPoint.cs

@@ -0,0 +1,2122 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Text;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC
+{
+    /**
+     * base class for points on elliptic curves.
+     */
+    internal abstract class ECPoint
+    {
+        protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0];
+
+        protected static ECFieldElement[] GetInitialZCoords(ECCurve curve)
+        {
+            // Cope with null curve, most commonly used by implicitlyCa
+            int coord = null == curve ? ECCurve.COORD_AFFINE : curve.CoordinateSystem;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                case ECCurve.COORD_LAMBDA_AFFINE:
+                    return EMPTY_ZS;
+                default:
+                    break;
+            }
+
+            ECFieldElement one = curve.FromBigInteger(BigInteger.One);
+
+            switch (coord)
+            {
+                case ECCurve.COORD_HOMOGENEOUS:
+                case ECCurve.COORD_JACOBIAN:
+                case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                    return new ECFieldElement[] { one };
+                case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+                    return new ECFieldElement[] { one, one, one };
+                case ECCurve.COORD_JACOBIAN_MODIFIED:
+                    return new ECFieldElement[] { one, curve.A };
+                default:
+                    throw new ArgumentException("unknown coordinate system");
+            }
+        }
+
+        protected internal readonly ECCurve m_curve;
+        protected internal readonly ECFieldElement m_x, m_y;
+        protected internal readonly ECFieldElement[] m_zs;
+        protected internal readonly bool m_withCompression;
+
+        // Dictionary is (string -> PreCompInfo)
+        protected internal IDictionary m_preCompTable = null;
+
+        protected ECPoint(ECCurve curve, ECFieldElement	x, ECFieldElement y, bool withCompression)
+            : this(curve, x, y, GetInitialZCoords(curve), withCompression)
+        {
+        }
+
+        internal ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+        {
+            this.m_curve = curve;
+            this.m_x = x;
+            this.m_y = y;
+            this.m_zs = zs;
+            this.m_withCompression = withCompression;
+        }
+
+        protected abstract bool SatisfiesCurveEquation();
+
+        protected virtual bool SatisfiesOrder()
+        {
+            if (BigInteger.One.Equals(Curve.Cofactor))
+                return true;
+
+            BigInteger n = Curve.Order;
+
+            // TODO Require order to be available for all curves
+
+            return n == null || ECAlgorithms.ReferenceMultiply(this, n).IsInfinity;
+        }
+
+        public ECPoint GetDetachedPoint()
+        {
+            return Normalize().Detach();
+        }
+
+        public virtual ECCurve Curve
+        {
+            get { return m_curve; }
+        }
+
+        protected abstract ECPoint Detach();
+
+        protected virtual int CurveCoordinateSystem
+        {
+            get
+            {
+                // Cope with null curve, most commonly used by implicitlyCa
+                return null == m_curve ? ECCurve.COORD_AFFINE : m_curve.CoordinateSystem;
+            }
+        }
+
+        /**
+         * Returns the affine x-coordinate after checking that this point is normalized.
+         * 
+         * @return The affine x-coordinate of this point
+         * @throws IllegalStateException if the point is not normalized
+         */
+        public virtual ECFieldElement AffineXCoord
+        {
+            get
+            {
+                CheckNormalized();
+                return XCoord;
+            }
+        }
+
+        /**
+         * Returns the affine y-coordinate after checking that this point is normalized
+         * 
+         * @return The affine y-coordinate of this point
+         * @throws IllegalStateException if the point is not normalized
+         */
+        public virtual ECFieldElement AffineYCoord
+        {
+            get
+            {
+                CheckNormalized();
+                return YCoord;
+            }
+        }
+
+        /**
+         * Returns the x-coordinate.
+         * 
+         * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+         * affine coordinate system; use Normalize() to get a point where the coordinates have their
+         * affine values, or use AffineXCoord if you expect the point to already have been normalized.
+         * 
+         * @return the x-coordinate of this point
+         */
+        public virtual ECFieldElement XCoord
+        {
+            get { return m_x; }
+        }
+
+        /**
+         * Returns the y-coordinate.
+         * 
+         * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+         * affine coordinate system; use Normalize() to get a point where the coordinates have their
+         * affine values, or use AffineYCoord if you expect the point to already have been normalized.
+         * 
+         * @return the y-coordinate of this point
+         */
+        public virtual ECFieldElement YCoord
+        {
+            get { return m_y; }
+        }
+
+        public virtual ECFieldElement GetZCoord(int index)
+        {
+            return (index < 0 || index >= m_zs.Length) ? null : m_zs[index];
+        }
+
+        public virtual ECFieldElement[] GetZCoords()
+        {
+            int zsLen = m_zs.Length;
+            if (zsLen == 0)
+            {
+                return m_zs;
+            }
+            ECFieldElement[] copy = new ECFieldElement[zsLen];
+            Array.Copy(m_zs, 0, copy, 0, zsLen);
+            return copy;
+        }
+
+        protected internal ECFieldElement RawXCoord
+        {
+            get { return m_x; }
+        }
+
+        protected internal ECFieldElement RawYCoord
+        {
+            get { return m_y; }
+        }
+
+        protected internal ECFieldElement[] RawZCoords
+        {
+            get { return m_zs; }
+        }
+
+        protected virtual void CheckNormalized()
+        {
+            if (!IsNormalized())
+                throw new InvalidOperationException("point not in normal form");
+        }
+
+        public virtual bool IsNormalized()
+        {
+            int coord = this.CurveCoordinateSystem;
+
+            return coord == ECCurve.COORD_AFFINE
+                || coord == ECCurve.COORD_LAMBDA_AFFINE
+                || IsInfinity
+                || RawZCoords[0].IsOne;
+        }
+
+        /**
+         * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+         * coordinates reflect those of the equivalent point in an affine coordinate system.
+         * 
+         * @return a new ECPoint instance representing the same point, but with normalized coordinates
+         */
+        public virtual ECPoint Normalize()
+        {
+            if (this.IsInfinity)
+            {
+                return this;
+            }
+
+            switch (this.CurveCoordinateSystem)
+            {
+                case ECCurve.COORD_AFFINE:
+                case ECCurve.COORD_LAMBDA_AFFINE:
+                {
+                    return this;
+                }
+                default:
+                {
+                    ECFieldElement Z1 = RawZCoords[0];
+                    if (Z1.IsOne)
+                    {
+                        return this;
+                    }
+
+                    return Normalize(Z1.Invert());
+                }
+            }
+        }
+
+        internal virtual ECPoint Normalize(ECFieldElement zInv)
+        {
+            switch (this.CurveCoordinateSystem)
+            {
+                case ECCurve.COORD_HOMOGENEOUS:
+                case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                {
+                    return CreateScaledPoint(zInv, zInv);
+                }
+                case ECCurve.COORD_JACOBIAN:
+                case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+                case ECCurve.COORD_JACOBIAN_MODIFIED:
+                {
+                    ECFieldElement zInv2 = zInv.Square(), zInv3 = zInv2.Multiply(zInv);
+                    return CreateScaledPoint(zInv2, zInv3);
+                }
+                default:
+                {
+                    throw new InvalidOperationException("not a projective coordinate system");
+                }
+            }
+        }
+
+        protected virtual ECPoint CreateScaledPoint(ECFieldElement sx, ECFieldElement sy)
+        {
+            return Curve.CreateRawPoint(RawXCoord.Multiply(sx), RawYCoord.Multiply(sy), IsCompressed);
+        }
+
+        public bool IsInfinity
+        {
+            get { return m_x == null && m_y == null; }
+        }
+
+        public bool IsCompressed
+        {
+            get { return m_withCompression; }
+        }
+
+        public bool IsValid()
+        {
+            return ImplIsValid(false, true);
+        }
+
+        internal bool IsValidPartial()
+        {
+            return ImplIsValid(false, false);
+        }
+
+        internal bool ImplIsValid(bool decompressed, bool checkOrder)
+        {
+            if (IsInfinity)
+                return true;
+
+            ValidityCallback callback = new ValidityCallback(this, decompressed, checkOrder);
+            ValidityPreCompInfo validity = (ValidityPreCompInfo)Curve.Precompute(this, ValidityPreCompInfo.PRECOMP_NAME, callback);
+            return !validity.HasFailed();
+        }
+
+        public virtual ECPoint ScaleX(ECFieldElement scale)
+        {
+            return IsInfinity
+                ? this
+                : Curve.CreateRawPoint(RawXCoord.Multiply(scale), RawYCoord, RawZCoords, IsCompressed);
+        }
+
+        public virtual ECPoint ScaleY(ECFieldElement scale)
+        {
+            return IsInfinity
+                ? this
+                : Curve.CreateRawPoint(RawXCoord, RawYCoord.Multiply(scale), RawZCoords, IsCompressed);
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as ECPoint);
+        }
+
+        public virtual bool Equals(ECPoint other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+
+            ECCurve c1 = this.Curve, c2 = other.Curve;
+            bool n1 = (null == c1), n2 = (null == c2);
+            bool i1 = IsInfinity, i2 = other.IsInfinity;
+
+            if (i1 || i2)
+            {
+                return (i1 && i2) && (n1 || n2 || c1.Equals(c2));
+            }
+
+            ECPoint p1 = this, p2 = other;
+            if (n1 && n2)
+            {
+                // Points with null curve are in affine form, so already normalized
+            }
+            else if (n1)
+            {
+                p2 = p2.Normalize();
+            }
+            else if (n2)
+            {
+                p1 = p1.Normalize();
+            }
+            else if (!c1.Equals(c2))
+            {
+                return false;
+            }
+            else
+            {
+                // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+                ECPoint[] points = new ECPoint[] { this, c1.ImportPoint(p2) };
+
+                // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal
+                c1.NormalizeAll(points);
+
+                p1 = points[0];
+                p2 = points[1];
+            }
+
+            return p1.XCoord.Equals(p2.XCoord) && p1.YCoord.Equals(p2.YCoord);
+        }
+
+        public override int GetHashCode()
+        {
+            ECCurve c = this.Curve;
+            int hc = (null == c) ? 0 : ~c.GetHashCode();
+
+            if (!this.IsInfinity)
+            {
+                // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+                ECPoint p = Normalize();
+
+                hc ^= p.XCoord.GetHashCode() * 17;
+                hc ^= p.YCoord.GetHashCode() * 257;
+            }
+
+            return hc;
+        }
+
+        public override string ToString()
+        {
+            if (this.IsInfinity)
+            {
+                return "INF";
+            }
+
+            StringBuilder sb = new StringBuilder();
+            sb.Append('(');
+            sb.Append(RawXCoord);
+            sb.Append(',');
+            sb.Append(RawYCoord);
+            for (int i = 0; i < m_zs.Length; ++i)
+            {
+                sb.Append(',');
+                sb.Append(m_zs[i]);
+            }
+            sb.Append(')');
+            return sb.ToString();
+        }
+
+        public virtual byte[] GetEncoded()
+        {
+            return GetEncoded(m_withCompression);
+        }
+
+        public abstract byte[] GetEncoded(bool compressed);
+
+        protected internal abstract bool CompressionYTilde { get; }
+
+        public abstract ECPoint Add(ECPoint b);
+        public abstract ECPoint Subtract(ECPoint b);
+        public abstract ECPoint Negate();
+
+        public virtual ECPoint TimesPow2(int e)
+        {
+            if (e < 0)
+                throw new ArgumentException("cannot be negative", "e");
+
+            ECPoint p = this;
+            while (--e >= 0)
+            {
+                p = p.Twice();
+            }
+            return p;
+        }
+
+        public abstract ECPoint Twice();
+        public abstract ECPoint Multiply(BigInteger b);
+
+        public virtual ECPoint TwicePlus(ECPoint b)
+        {
+            return Twice().Add(b);
+        }
+
+        public virtual ECPoint ThreeTimes()
+        {
+            return TwicePlus(this);
+        }
+
+        private class ValidityCallback
+            : IPreCompCallback
+        {
+            private readonly ECPoint m_outer;
+            private readonly bool m_decompressed, m_checkOrder;
+
+            internal ValidityCallback(ECPoint outer, bool decompressed, bool checkOrder)
+            {
+                this.m_outer = outer;
+                this.m_decompressed = decompressed;
+                this.m_checkOrder = checkOrder;
+            }
+
+            public PreCompInfo Precompute(PreCompInfo existing)
+            {
+                ValidityPreCompInfo info = existing as ValidityPreCompInfo;
+                if (info == null)
+                {
+                    info = new ValidityPreCompInfo();
+                }
+
+                if (info.HasFailed())
+                    return info;
+
+                if (!info.HasCurveEquationPassed())
+                {
+                    if (!m_decompressed && !m_outer.SatisfiesCurveEquation())
+                    {
+                        info.ReportFailed();
+                        return info;
+                    }
+                    info.ReportCurveEquationPassed();
+                }
+                if (m_checkOrder && !info.HasOrderPassed())
+                {
+                    if (!m_outer.SatisfiesOrder())
+                    {
+                        info.ReportFailed();
+                        return info;
+                    }
+                    info.ReportOrderPassed();
+                }
+                return info;
+            }
+        }
+    }
+
+    internal abstract class ECPointBase
+        : ECPoint
+    {
+        protected internal ECPointBase(
+            ECCurve			curve,
+            ECFieldElement	x,
+            ECFieldElement	y,
+            bool			withCompression)
+            : base(curve, x, y, withCompression)
+        {
+        }
+
+        protected internal ECPointBase(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        /**
+         * return the field element encoded with point compression. (S 4.3.6)
+         */
+        public override byte[] GetEncoded(bool compressed)
+        {
+            if (this.IsInfinity)
+            {
+                return new byte[1];
+            }
+
+            ECPoint normed = Normalize();
+
+            byte[] X = normed.XCoord.GetEncoded();
+
+            if (compressed)
+            {
+                byte[] PO = new byte[X.Length + 1];
+                PO[0] = (byte)(normed.CompressionYTilde ? 0x03 : 0x02);
+                Array.Copy(X, 0, PO, 1, X.Length);
+                return PO;
+            }
+
+            byte[] Y = normed.YCoord.GetEncoded();
+
+            {
+                byte[] PO = new byte[X.Length + Y.Length + 1];
+                PO[0] = 0x04;
+                Array.Copy(X, 0, PO, 1, X.Length);
+                Array.Copy(Y, 0, PO, X.Length + 1, Y.Length);
+                return PO;
+            }
+        }
+
+        /**
+         * Multiplies this <code>ECPoint</code> by the given number.
+         * @param k The multiplicator.
+         * @return <code>k * this</code>.
+         */
+        public override ECPoint Multiply(BigInteger k)
+        {
+            return this.Curve.GetMultiplier().Multiply(this, k);
+        }
+    }
+
+    internal abstract class AbstractFpPoint
+        : ECPointBase
+    {
+        protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression)
+            : base(curve, x, y, withCompression)
+        {
+        }
+
+        protected AbstractFpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected internal override bool CompressionYTilde
+        {
+            get { return this.AffineYCoord.TestBitZero(); }
+        }
+
+        protected override bool SatisfiesCurveEquation()
+        {
+            ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = Curve.A, B = Curve.B;
+            ECFieldElement lhs = Y.Square();
+
+            switch (CurveCoordinateSystem)
+            {
+            case ECCurve.COORD_AFFINE:
+                break;
+            case ECCurve.COORD_HOMOGENEOUS:
+            {
+                ECFieldElement Z = this.RawZCoords[0];
+                if (!Z.IsOne)
+                {
+                    ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2);
+                    lhs = lhs.Multiply(Z);
+                    A = A.Multiply(Z2);
+                    B = B.Multiply(Z3);
+                }
+                break;
+            }
+            case ECCurve.COORD_JACOBIAN:
+            case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+            case ECCurve.COORD_JACOBIAN_MODIFIED:
+            {
+                ECFieldElement Z = this.RawZCoords[0];
+                if (!Z.IsOne)
+                {
+                    ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square(), Z6 = Z2.Multiply(Z4);
+                    A = A.Multiply(Z4);
+                    B = B.Multiply(Z6);
+                }
+                break;
+            }
+            default:
+                throw new InvalidOperationException("unsupported coordinate system");
+            }
+
+            ECFieldElement rhs = X.Square().Add(A).Multiply(X).Add(B);
+            return lhs.Equals(rhs);
+        }
+
+        public override ECPoint Subtract(ECPoint b)
+        {
+            if (b.IsInfinity)
+                return this;
+
+            // Add -b
+            return Add(b.Negate());
+        }
+    }
+
+    /**
+     * Elliptic curve points over Fp
+     */
+    internal class FpPoint
+        : AbstractFpPoint
+    {
+        /**
+         * Create a point which encodes without point compression.
+         *
+         * @param curve the curve to use
+         * @param x affine x co-ordinate
+         * @param y affine y co-ordinate
+         */
+        public FpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y)
+            : this(curve, x, y, false)
+        {
+        }
+
+        /**
+         * Create a point that encodes with or without point compression.
+         *
+         * @param curve the curve to use
+         * @param x affine x co-ordinate
+         * @param y affine y co-ordinate
+         * @param withCompression if true encode with point compression
+         */
+        public FpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression)
+            : base(curve, x, y, withCompression)
+        {
+            if ((x == null) != (y == null))
+                throw new ArgumentException("Exactly one of the field elements is null");
+        }
+
+        internal FpPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new FpPoint(null, AffineXCoord, AffineYCoord, false);
+        }
+
+        public override ECFieldElement GetZCoord(int index)
+        {
+            if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.CurveCoordinateSystem)
+            {
+                return GetJacobianModifiedW();
+            }
+
+            return base.GetZCoord(index);
+        }
+
+        // B.3 pg 62
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+            if (this == b)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+            int coord = curve.CoordinateSystem;
+
+            ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord;
+            ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                {
+                    ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1);
+
+                    if (dx.IsZero)
+                    {
+                        if (dy.IsZero)
+                        {
+                            // this == b, i.e. this must be doubled
+                            return Twice();
+                        }
+
+                        // this == -b, i.e. the result is the point at infinity
+                        return Curve.Infinity;
+                    }
+
+                    ECFieldElement gamma = dy.Divide(dx);
+                    ECFieldElement X3 = gamma.Square().Subtract(X1).Subtract(X2);
+                    ECFieldElement Y3 = gamma.Multiply(X1.Subtract(X3)).Subtract(Y1);
+
+                    return new FpPoint(Curve, X3, Y3, IsCompressed);
+                }
+
+                case ECCurve.COORD_HOMOGENEOUS:
+                {
+                    ECFieldElement Z1 = this.RawZCoords[0];
+                    ECFieldElement Z2 = b.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+                    bool Z2IsOne = Z2.IsOne;
+
+                    ECFieldElement u1 = Z1IsOne ? Y2 : Y2.Multiply(Z1);
+                    ECFieldElement u2 = Z2IsOne ? Y1 : Y1.Multiply(Z2);
+                    ECFieldElement u = u1.Subtract(u2);
+                    ECFieldElement v1 = Z1IsOne ? X2 : X2.Multiply(Z1);
+                    ECFieldElement v2 = Z2IsOne ? X1 : X1.Multiply(Z2);
+                    ECFieldElement v = v1.Subtract(v2);
+
+                    // Check if b == this or b == -this
+                    if (v.IsZero)
+                    {
+                        if (u.IsZero)
+                        {
+                            // this == b, i.e. this must be doubled
+                            return this.Twice();
+                        }
+
+                        // this == -b, i.e. the result is the point at infinity
+                        return curve.Infinity;
+                    }
+
+                    // TODO Optimize for when w == 1
+                    ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.Multiply(Z2);
+                    ECFieldElement vSquared = v.Square();
+                    ECFieldElement vCubed = vSquared.Multiply(v);
+                    ECFieldElement vSquaredV2 = vSquared.Multiply(v2);
+                    ECFieldElement A = u.Square().Multiply(w).Subtract(vCubed).Subtract(Two(vSquaredV2));
+
+                    ECFieldElement X3 = v.Multiply(A);
+                    ECFieldElement Y3 = vSquaredV2.Subtract(A).MultiplyMinusProduct(u, u2, vCubed);
+                    ECFieldElement Z3 = vCubed.Multiply(w);
+
+                    return new FpPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+
+                case ECCurve.COORD_JACOBIAN:
+                case ECCurve.COORD_JACOBIAN_MODIFIED:
+                {
+                    ECFieldElement Z1 = this.RawZCoords[0];
+                    ECFieldElement Z2 = b.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+
+                    ECFieldElement X3, Y3, Z3, Z3Squared = null;
+
+                    if (!Z1IsOne && Z1.Equals(Z2))
+                    {
+                        // TODO Make this available as public method coZAdd?
+
+                        ECFieldElement dx = X1.Subtract(X2), dy = Y1.Subtract(Y2);
+                        if (dx.IsZero)
+                        {
+                            if (dy.IsZero)
+                            {
+                                return Twice();
+                            }
+                            return curve.Infinity;
+                        }
+
+                        ECFieldElement C = dx.Square();
+                        ECFieldElement W1 = X1.Multiply(C), W2 = X2.Multiply(C);
+                        ECFieldElement A1 = W1.Subtract(W2).Multiply(Y1);
+
+                        X3 = dy.Square().Subtract(W1).Subtract(W2);
+                        Y3 = W1.Subtract(X3).Multiply(dy).Subtract(A1);
+                        Z3 = dx;
+
+                        if (Z1IsOne)
+                        {
+                            Z3Squared = C;
+                        }
+                        else
+                        {
+                            Z3 = Z3.Multiply(Z1);
+                        }
+                    }
+                    else
+                    {
+                        ECFieldElement Z1Squared, U2, S2;
+                        if (Z1IsOne)
+                        {
+                            Z1Squared = Z1; U2 = X2; S2 = Y2;
+                        }
+                        else
+                        {
+                            Z1Squared = Z1.Square();
+                            U2 = Z1Squared.Multiply(X2);
+                            ECFieldElement Z1Cubed = Z1Squared.Multiply(Z1);
+                            S2 = Z1Cubed.Multiply(Y2);
+                        }
+
+                        bool Z2IsOne = Z2.IsOne;
+                        ECFieldElement Z2Squared, U1, S1;
+                        if (Z2IsOne)
+                        {
+                            Z2Squared = Z2; U1 = X1; S1 = Y1;
+                        }
+                        else
+                        {
+                            Z2Squared = Z2.Square();
+                            U1 = Z2Squared.Multiply(X1);
+                            ECFieldElement Z2Cubed = Z2Squared.Multiply(Z2);
+                            S1 = Z2Cubed.Multiply(Y1);
+                        }
+
+                        ECFieldElement H = U1.Subtract(U2);
+                        ECFieldElement R = S1.Subtract(S2);
+
+                        // Check if b == this or b == -this
+                        if (H.IsZero)
+                        {
+                            if (R.IsZero)
+                            {
+                                // this == b, i.e. this must be doubled
+                                return this.Twice();
+                            }
+
+                            // this == -b, i.e. the result is the point at infinity
+                            return curve.Infinity;
+                        }
+
+                        ECFieldElement HSquared = H.Square();
+                        ECFieldElement G = HSquared.Multiply(H);
+                        ECFieldElement V = HSquared.Multiply(U1);
+
+                        X3 = R.Square().Add(G).Subtract(Two(V));
+                        Y3 = V.Subtract(X3).MultiplyMinusProduct(R, G, S1);
+
+                        Z3 = H;
+                        if (!Z1IsOne)
+                        {
+                            Z3 = Z3.Multiply(Z1);
+                        }
+                        if (!Z2IsOne)
+                        {
+                            Z3 = Z3.Multiply(Z2);
+                        }
+
+                        // Alternative calculation of Z3 using fast square
+                        //X3 = four(X3);
+                        //Y3 = eight(Y3);
+                        //Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).Multiply(H);
+
+                        if (Z3 == H)
+                        {
+                            Z3Squared = HSquared;
+                        }
+                    }
+
+                    ECFieldElement[] zs;
+                    if (coord == ECCurve.COORD_JACOBIAN_MODIFIED)
+                    {
+                        // TODO If the result will only be used in a subsequent addition, we don't need W3
+                        ECFieldElement W3 = CalculateJacobianModifiedW(Z3, Z3Squared);
+
+                        zs = new ECFieldElement[] { Z3, W3 };
+                    }
+                    else
+                    {
+                        zs = new ECFieldElement[] { Z3 };
+                    }
+
+                    return new FpPoint(curve, X3, Y3, zs, IsCompressed);
+                }
+
+                default:
+                {
+                    throw new InvalidOperationException("unsupported coordinate system");
+                }
+            }
+        }
+
+        // B.3 pg 62
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            ECFieldElement Y1 = this.RawYCoord;
+            if (Y1.IsZero) 
+                return curve.Infinity;
+
+            int coord = curve.CoordinateSystem;
+
+            ECFieldElement X1 = this.RawXCoord;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                {
+                    ECFieldElement X1Squared = X1.Square();
+                    ECFieldElement gamma = Three(X1Squared).Add(this.Curve.A).Divide(Two(Y1));
+                    ECFieldElement X3 = gamma.Square().Subtract(Two(X1));
+                    ECFieldElement Y3 = gamma.Multiply(X1.Subtract(X3)).Subtract(Y1);
+
+                    return new FpPoint(Curve, X3, Y3, IsCompressed);
+                }
+
+                case ECCurve.COORD_HOMOGENEOUS:
+                {
+                    ECFieldElement Z1 = this.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+
+                    // TODO Optimize for small negative a4 and -3
+                    ECFieldElement w = curve.A;
+                    if (!w.IsZero && !Z1IsOne)
+                    {
+                        w = w.Multiply(Z1.Square());
+                    }
+                    w = w.Add(Three(X1.Square()));
+
+                    ECFieldElement s = Z1IsOne ? Y1 : Y1.Multiply(Z1);
+                    ECFieldElement t = Z1IsOne ? Y1.Square() : s.Multiply(Y1);
+                    ECFieldElement B = X1.Multiply(t);
+                    ECFieldElement _4B = Four(B);
+                    ECFieldElement h = w.Square().Subtract(Two(_4B));
+
+                    ECFieldElement _2s = Two(s);
+                    ECFieldElement X3 = h.Multiply(_2s);
+                    ECFieldElement _2t = Two(t);
+                    ECFieldElement Y3 = _4B.Subtract(h).Multiply(w).Subtract(Two(_2t.Square()));
+                    ECFieldElement _4sSquared = Z1IsOne ? Two(_2t) : _2s.Square();
+                    ECFieldElement Z3 = Two(_4sSquared).Multiply(s);
+
+                    return new FpPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+
+                case ECCurve.COORD_JACOBIAN:
+                {
+                    ECFieldElement Z1 = this.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+
+                    ECFieldElement Y1Squared = Y1.Square();
+                    ECFieldElement T = Y1Squared.Square();
+
+                    ECFieldElement a4 = curve.A;
+                    ECFieldElement a4Neg = a4.Negate();
+
+                    ECFieldElement M, S;
+                    if (a4Neg.ToBigInteger().Equals(BigInteger.ValueOf(3)))
+                    {
+                        ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.Square();
+                        M = Three(X1.Add(Z1Squared).Multiply(X1.Subtract(Z1Squared)));
+                        S = Four(Y1Squared.Multiply(X1));
+                    }
+                    else
+                    {
+                        ECFieldElement X1Squared = X1.Square();
+                        M = Three(X1Squared);
+                        if (Z1IsOne)
+                        {
+                            M = M.Add(a4);
+                        }
+                        else if (!a4.IsZero)
+                        {
+                            ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.Square();
+                            ECFieldElement Z1Pow4 = Z1Squared.Square();
+                            if (a4Neg.BitLength < a4.BitLength)
+                            {
+                                M = M.Subtract(Z1Pow4.Multiply(a4Neg));
+                            }
+                            else
+                            {
+                                M = M.Add(Z1Pow4.Multiply(a4));
+                            }
+                        }
+                        //S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T));
+                        S = Four(X1.Multiply(Y1Squared));
+                    }
+
+                    ECFieldElement X3 = M.Square().Subtract(Two(S));
+                    ECFieldElement Y3 = S.Subtract(X3).Multiply(M).Subtract(Eight(T));
+
+                    ECFieldElement Z3 = Two(Y1);
+                    if (!Z1IsOne)
+                    {
+                        Z3 = Z3.Multiply(Z1);
+                    }
+
+                    // Alternative calculation of Z3 using fast square
+                    //ECFieldElement Z3 = doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared);
+
+                    return new FpPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+
+                case ECCurve.COORD_JACOBIAN_MODIFIED:
+                {
+                    return TwiceJacobianModified(true);
+                }
+
+                default:
+                {
+                    throw new InvalidOperationException("unsupported coordinate system");
+                }
+            }
+        }
+
+        public override ECPoint TwicePlus(ECPoint b)
+        {
+            if (this == b)
+                return ThreeTimes();
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return Twice();
+
+            ECFieldElement Y1 = this.RawYCoord;
+            if (Y1.IsZero)
+                return b;
+
+            ECCurve curve = this.Curve;
+            int coord = curve.CoordinateSystem;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                {
+                    ECFieldElement X1 = this.RawXCoord;
+                    ECFieldElement X2 = b.RawXCoord, Y2 = b.RawYCoord;
+
+                    ECFieldElement dx = X2.Subtract(X1), dy = Y2.Subtract(Y1);
+
+                    if (dx.IsZero)
+                    {
+                        if (dy.IsZero)
+                        {
+                            // this == b i.e. the result is 3P
+                            return ThreeTimes();
+                        }
+
+                        // this == -b, i.e. the result is P
+                        return this;
+                    }
+
+                    /*
+                     * Optimized calculation of 2P + Q, as described in "Trading Inversions for
+                     * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery.
+                     */
+
+                    ECFieldElement X = dx.Square(), Y = dy.Square();
+                    ECFieldElement d = X.Multiply(Two(X1).Add(X2)).Subtract(Y);
+                    if (d.IsZero)
+                    {
+                        return Curve.Infinity;
+                    }
+
+                    ECFieldElement D = d.Multiply(dx);
+                    ECFieldElement I = D.Invert();
+                    ECFieldElement L1 = d.Multiply(I).Multiply(dy);
+                    ECFieldElement L2 = Two(Y1).Multiply(X).Multiply(dx).Multiply(I).Subtract(L1);
+                    ECFieldElement X4 = (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(X2);
+                    ECFieldElement Y4 = (X1.Subtract(X4)).Multiply(L2).Subtract(Y1);
+
+                    return new FpPoint(Curve, X4, Y4, IsCompressed);
+                }
+                case ECCurve.COORD_JACOBIAN_MODIFIED:
+                {
+                    return TwiceJacobianModified(false).Add(b);
+                }
+                default:
+                {
+                    return Twice().Add(b);
+                }
+            }
+        }
+
+        public override ECPoint ThreeTimes()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECFieldElement Y1 = this.RawYCoord;
+            if (Y1.IsZero)
+                return this;
+
+            ECCurve curve = this.Curve;
+            int coord = curve.CoordinateSystem;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                {
+                    ECFieldElement X1 = this.RawXCoord;
+
+                    ECFieldElement _2Y1 = Two(Y1);
+                    ECFieldElement X = _2Y1.Square();
+                    ECFieldElement Z = Three(X1.Square()).Add(Curve.A);
+                    ECFieldElement Y = Z.Square();
+
+                    ECFieldElement d = Three(X1).Multiply(X).Subtract(Y);
+                    if (d.IsZero)
+                    {
+                        return Curve.Infinity;
+                    }
+
+                    ECFieldElement D = d.Multiply(_2Y1);
+                    ECFieldElement I = D.Invert();
+                    ECFieldElement L1 = d.Multiply(I).Multiply(Z);
+                    ECFieldElement L2 = X.Square().Multiply(I).Subtract(L1);
+
+                    ECFieldElement X4 = (L2.Subtract(L1)).Multiply(L1.Add(L2)).Add(X1);
+                    ECFieldElement Y4 = (X1.Subtract(X4)).Multiply(L2).Subtract(Y1);
+                    return new FpPoint(Curve, X4, Y4, IsCompressed);
+                }
+                case ECCurve.COORD_JACOBIAN_MODIFIED:
+                {
+                    return TwiceJacobianModified(false).Add(this);
+                }
+                default:
+                {
+                    // NOTE: Be careful about recursions between TwicePlus and ThreeTimes
+                    return Twice().Add(this);
+                }
+            }
+        }
+
+        public override ECPoint TimesPow2(int e)
+        {
+            if (e < 0)
+                throw new ArgumentException("cannot be negative", "e");
+            if (e == 0 || this.IsInfinity)
+                return this;
+            if (e == 1)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            ECFieldElement Y1 = this.RawYCoord;
+            if (Y1.IsZero) 
+                return curve.Infinity;
+
+            int coord = curve.CoordinateSystem;
+
+            ECFieldElement W1 = curve.A;
+            ECFieldElement X1 = this.RawXCoord;
+            ECFieldElement Z1 = this.RawZCoords.Length < 1 ? curve.FromBigInteger(BigInteger.One) : this.RawZCoords[0];
+
+            if (!Z1.IsOne)
+            {
+                switch (coord)
+                {
+                case ECCurve.COORD_HOMOGENEOUS:
+                    ECFieldElement Z1Sq = Z1.Square();
+                    X1 = X1.Multiply(Z1);
+                    Y1 = Y1.Multiply(Z1Sq);
+                    W1 = CalculateJacobianModifiedW(Z1, Z1Sq);
+                    break;
+                case ECCurve.COORD_JACOBIAN:
+                    W1 = CalculateJacobianModifiedW(Z1, null);
+                    break;
+                case ECCurve.COORD_JACOBIAN_MODIFIED:
+                    W1 = GetJacobianModifiedW();
+                    break;
+                }
+            }
+
+            for (int i = 0; i < e; ++i)
+            {
+                if (Y1.IsZero) 
+                    return curve.Infinity;
+
+                ECFieldElement X1Squared = X1.Square();
+                ECFieldElement M = Three(X1Squared);
+                ECFieldElement _2Y1 = Two(Y1);
+                ECFieldElement _2Y1Squared = _2Y1.Multiply(Y1);
+                ECFieldElement S = Two(X1.Multiply(_2Y1Squared));
+                ECFieldElement _4T = _2Y1Squared.Square();
+                ECFieldElement _8T = Two(_4T);
+
+                if (!W1.IsZero)
+                {
+                    M = M.Add(W1);
+                    W1 = Two(_8T.Multiply(W1));
+                }
+
+                X1 = M.Square().Subtract(Two(S));
+                Y1 = M.Multiply(S.Subtract(X1)).Subtract(_8T);
+                Z1 = Z1.IsOne ? _2Y1 : _2Y1.Multiply(Z1);
+            }
+
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+                ECFieldElement zInv = Z1.Invert(), zInv2 = zInv.Square(), zInv3 = zInv2.Multiply(zInv);
+                return new FpPoint(curve, X1.Multiply(zInv2), Y1.Multiply(zInv3), IsCompressed);
+            case ECCurve.COORD_HOMOGENEOUS:
+                X1 = X1.Multiply(Z1);
+                Z1 = Z1.Multiply(Z1.Square());
+                return new FpPoint(curve, X1, Y1, new ECFieldElement[] { Z1 }, IsCompressed);
+            case ECCurve.COORD_JACOBIAN:
+                return new FpPoint(curve, X1, Y1, new ECFieldElement[] { Z1 }, IsCompressed);
+            case ECCurve.COORD_JACOBIAN_MODIFIED:
+                return new FpPoint(curve, X1, Y1, new ECFieldElement[] { Z1, W1 }, IsCompressed);
+            default:
+                throw new InvalidOperationException("unsupported coordinate system");
+            }
+        }
+
+        protected virtual ECFieldElement Two(ECFieldElement x)
+        {
+            return x.Add(x);
+        }
+
+        protected virtual ECFieldElement Three(ECFieldElement x)
+        {
+            return Two(x).Add(x);
+        }
+
+        protected virtual ECFieldElement Four(ECFieldElement x)
+        {
+            return Two(Two(x));
+        }
+
+        protected virtual ECFieldElement Eight(ECFieldElement x)
+        {
+            return Four(Two(x));
+        }
+
+        protected virtual ECFieldElement DoubleProductFromSquares(ECFieldElement a, ECFieldElement b,
+            ECFieldElement aSquared, ECFieldElement bSquared)
+        {
+            /*
+             * NOTE: If squaring in the field is faster than multiplication, then this is a quicker
+             * way to calculate 2.A.B, if A^2 and B^2 are already known.
+             */
+            return a.Add(b).Square().Subtract(aSquared).Subtract(bSquared);
+        }
+
+        public override ECPoint Negate()
+        {
+            if (IsInfinity)
+                return this;
+
+            ECCurve curve = Curve;
+            int coord = curve.CoordinateSystem;
+
+            if (ECCurve.COORD_AFFINE != coord)
+            {
+                return new FpPoint(curve, RawXCoord, RawYCoord.Negate(), RawZCoords, IsCompressed);
+            }
+
+            return new FpPoint(curve, RawXCoord, RawYCoord.Negate(), IsCompressed);
+        }
+
+        protected virtual ECFieldElement CalculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared)
+        {
+            ECFieldElement a4 = this.Curve.A;
+            if (a4.IsZero || Z.IsOne)
+                return a4;
+
+            if (ZSquared == null)
+            {
+                ZSquared = Z.Square();
+            }
+
+            ECFieldElement W = ZSquared.Square();
+            ECFieldElement a4Neg = a4.Negate();
+            if (a4Neg.BitLength < a4.BitLength)
+            {
+                W = W.Multiply(a4Neg).Negate();
+            }
+            else
+            {
+                W = W.Multiply(a4);
+            }
+            return W;
+        }
+
+        protected virtual ECFieldElement GetJacobianModifiedW()
+        {
+            ECFieldElement[] ZZ = this.RawZCoords;
+            ECFieldElement W = ZZ[1];
+            if (W == null)
+            {
+                // NOTE: Rarely, TwicePlus will result in the need for a lazy W1 calculation here
+                ZZ[1] = W = CalculateJacobianModifiedW(ZZ[0], null);
+            }
+            return W;
+        }
+
+        protected virtual FpPoint TwiceJacobianModified(bool calculateW)
+        {
+            ECFieldElement X1 = this.RawXCoord, Y1 = this.RawYCoord, Z1 = this.RawZCoords[0], W1 = GetJacobianModifiedW();
+
+            ECFieldElement X1Squared = X1.Square();
+            ECFieldElement M = Three(X1Squared).Add(W1);
+            ECFieldElement _2Y1 = Two(Y1);
+            ECFieldElement _2Y1Squared = _2Y1.Multiply(Y1);
+            ECFieldElement S = Two(X1.Multiply(_2Y1Squared));
+            ECFieldElement X3 = M.Square().Subtract(Two(S));
+            ECFieldElement _4T = _2Y1Squared.Square();
+            ECFieldElement _8T = Two(_4T);
+            ECFieldElement Y3 = M.Multiply(S.Subtract(X3)).Subtract(_8T);
+            ECFieldElement W3 = calculateW ? Two(_8T.Multiply(W1)) : null;
+            ECFieldElement Z3 = Z1.IsOne ? _2Y1 : _2Y1.Multiply(Z1);
+
+            return new FpPoint(this.Curve, X3, Y3, new ECFieldElement[] { Z3, W3 }, IsCompressed);
+        }
+    }
+
+    internal abstract class AbstractF2mPoint 
+        : ECPointBase
+    {
+        protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, bool withCompression)
+            : base(curve, x, y, withCompression)
+        {
+        }
+
+        protected AbstractF2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override bool SatisfiesCurveEquation()
+        {
+            ECCurve curve = Curve;
+            ECFieldElement X = this.RawXCoord, Y = this.RawYCoord, A = curve.A, B = curve.B;
+            ECFieldElement lhs, rhs;
+
+            int coord = curve.CoordinateSystem;
+            if (coord == ECCurve.COORD_LAMBDA_PROJECTIVE)
+            {
+                ECFieldElement Z = this.RawZCoords[0];
+                bool ZIsOne = Z.IsOne;
+
+                if (X.IsZero)
+                {
+                    // NOTE: For x == 0, we expect the affine-y instead of the lambda-y 
+                    lhs = Y.Square();
+                    rhs = B;
+                    if (!ZIsOne)
+                    {
+                        ECFieldElement Z2 = Z.Square();
+                        rhs = rhs.Multiply(Z2);
+                    }
+                }
+                else
+                {
+                    ECFieldElement L = Y, X2 = X.Square();
+                    if (ZIsOne)
+                    {
+                        lhs = L.Square().Add(L).Add(A);
+                        rhs = X2.Square().Add(B);
+                    }
+                    else
+                    {
+                        ECFieldElement Z2 = Z.Square(), Z4 = Z2.Square();
+                        lhs = L.Add(Z).MultiplyPlusProduct(L, A, Z2);
+                        // TODO If sqrt(b) is precomputed this can be simplified to a single square
+                        rhs = X2.SquarePlusProduct(B, Z4);
+                    }
+                    lhs = lhs.Multiply(X2);
+                }
+            }
+            else
+            {
+                lhs = Y.Add(X).Multiply(Y);
+
+                switch (coord)
+                {
+                    case ECCurve.COORD_AFFINE:
+                        break;
+                    case ECCurve.COORD_HOMOGENEOUS:
+                        {
+                            ECFieldElement Z = this.RawZCoords[0];
+                            if (!Z.IsOne)
+                            {
+                                ECFieldElement Z2 = Z.Square(), Z3 = Z.Multiply(Z2);
+                                lhs = lhs.Multiply(Z);
+                                A = A.Multiply(Z);
+                                B = B.Multiply(Z3);
+                            }
+                            break;
+                        }
+                    default:
+                        throw new InvalidOperationException("unsupported coordinate system");
+                }
+
+                rhs = X.Add(A).Multiply(X.Square()).Add(B);
+            }
+
+            return lhs.Equals(rhs);
+        }
+
+        protected override bool SatisfiesOrder()
+        {
+            ECCurve curve = Curve;
+            BigInteger cofactor = curve.Cofactor;
+            if (BigInteger.Two.Equals(cofactor))
+            {
+                /*
+                 *  Check that the trace of (X + A) is 0, then there exists a solution to L^2 + L = X + A,
+                 *  and so a halving is possible, so this point is the double of another.  
+                 */
+                ECPoint N = this.Normalize();
+                ECFieldElement X = N.AffineXCoord;
+                ECFieldElement rhs = X.Add(curve.A);
+                return ((AbstractF2mFieldElement)rhs).Trace() == 0;
+            }
+            if (BigInteger.ValueOf(4).Equals(cofactor))
+            {
+                /*
+                 * Solve L^2 + L = X + A to find the half of this point, if it exists (fail if not).
+                 * Generate both possibilities for the square of the half-point's x-coordinate (w),
+                 * and check if Tr(w + A) == 0 for at least one; then a second halving is possible
+                 * (see comments for cofactor 2 above), so this point is four times another.
+                 * 
+                 * Note: Tr(x^2) == Tr(x). 
+                 */
+                ECPoint N = this.Normalize();
+                ECFieldElement X = N.AffineXCoord;
+                ECFieldElement lambda = ((AbstractF2mCurve)curve).SolveQuadraticEquation(X.Add(curve.A));
+                if (lambda == null)
+                    return false;
+
+                ECFieldElement w = X.Multiply(lambda).Add(N.AffineYCoord);
+                ECFieldElement t = w.Add(curve.A);
+                return ((AbstractF2mFieldElement)t).Trace() == 0
+                    || ((AbstractF2mFieldElement)(t.Add(X))).Trace() == 0;
+            }
+
+            return base.SatisfiesOrder();
+        }
+
+        public override ECPoint ScaleX(ECFieldElement scale)
+        {
+            if (this.IsInfinity)
+                return this;
+
+            switch (CurveCoordinateSystem)
+            {
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            {
+                // Y is actually Lambda (X + Y/X) here
+                ECFieldElement X = RawXCoord, L = RawYCoord;
+
+                ECFieldElement X2 = X.Multiply(scale);
+                ECFieldElement L2 = L.Add(X).Divide(scale).Add(X2);
+
+                return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed);
+            }
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                // Y is actually Lambda (X + Y/X) here
+                ECFieldElement X = RawXCoord, L = RawYCoord, Z = RawZCoords[0];
+
+                // We scale the Z coordinate also, to avoid an inversion
+                ECFieldElement X2 = X.Multiply(scale.Square());
+                ECFieldElement L2 = L.Add(X).Add(X2);
+                ECFieldElement Z2 = Z.Multiply(scale);
+
+                return Curve.CreateRawPoint(X, L2, new ECFieldElement[] { Z2 }, IsCompressed);
+            }
+            default:
+            {
+                return base.ScaleX(scale);
+            }
+            }
+        }
+
+        public override ECPoint ScaleY(ECFieldElement scale)
+        {
+            if (this.IsInfinity)
+                return this;
+
+            switch (CurveCoordinateSystem)
+            {
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                ECFieldElement X = RawXCoord, L = RawYCoord;
+
+                // Y is actually Lambda (X + Y/X) here
+                ECFieldElement L2 = L.Add(X).Multiply(scale).Add(X);
+
+                return Curve.CreateRawPoint(X, L2, RawZCoords, IsCompressed);
+            }
+            default:
+            {
+                return base.ScaleY(scale);
+            }
+            }
+        }
+
+        public override ECPoint Subtract(ECPoint b)
+        {
+            if (b.IsInfinity)
+                return this;
+
+            // Add -b
+            return Add(b.Negate());
+        }
+
+        public virtual AbstractF2mPoint Tau()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+            int coord = curve.CoordinateSystem;
+
+            ECFieldElement X1 = this.RawXCoord;
+
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            {
+                ECFieldElement Y1 = this.RawYCoord;
+                return (AbstractF2mPoint)curve.CreateRawPoint(X1.Square(), Y1.Square(), IsCompressed);
+            }
+            case ECCurve.COORD_HOMOGENEOUS:
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0];
+                return (AbstractF2mPoint)curve.CreateRawPoint(X1.Square(), Y1.Square(),
+                    new ECFieldElement[] { Z1.Square() }, IsCompressed);
+            }
+            default:
+            {
+                throw new InvalidOperationException("unsupported coordinate system");
+            }
+            }
+        }
+
+        public virtual AbstractF2mPoint TauPow(int pow)
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+            int coord = curve.CoordinateSystem;
+
+            ECFieldElement X1 = this.RawXCoord;
+
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            {
+                ECFieldElement Y1 = this.RawYCoord;
+                return (AbstractF2mPoint)curve.CreateRawPoint(X1.SquarePow(pow), Y1.SquarePow(pow), IsCompressed);
+            }
+            case ECCurve.COORD_HOMOGENEOUS:
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0];
+                return (AbstractF2mPoint)curve.CreateRawPoint(X1.SquarePow(pow), Y1.SquarePow(pow),
+                    new ECFieldElement[] { Z1.SquarePow(pow) }, IsCompressed);
+            }
+            default:
+            {
+                throw new InvalidOperationException("unsupported coordinate system");
+            }
+            }
+        }
+    }
+
+    /**
+     * Elliptic curve points over F2m
+     */
+    internal class F2mPoint
+        : AbstractF2mPoint
+    {
+        /**
+         * @param curve base curve
+         * @param x x point
+         * @param y y point
+         */
+        public F2mPoint(
+            ECCurve			curve,
+            ECFieldElement	x,
+            ECFieldElement	y)
+            :  this(curve, x, y, false)
+        {
+        }
+
+        /**
+         * @param curve base curve
+         * @param x x point
+         * @param y y point
+         * @param withCompression true if encode with point compression.
+         */
+        public F2mPoint(
+            ECCurve			curve,
+            ECFieldElement	x,
+            ECFieldElement	y,
+            bool			withCompression)
+            : base(curve, x, y, withCompression)
+        {
+            if ((x == null) != (y == null))
+            {
+                throw new ArgumentException("Exactly one of the field elements is null");
+            }
+
+            if (x != null)
+            {
+                // Check if x and y are elements of the same field
+                F2mFieldElement.CheckFieldElements(x, y);
+
+                // Check if x and a are elements of the same field
+                if (curve != null)
+                {
+                    F2mFieldElement.CheckFieldElements(x, curve.A);
+                }
+            }
+        }
+
+        internal F2mPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, bool withCompression)
+            : base(curve, x, y, zs, withCompression)
+        {
+        }
+
+        protected override ECPoint Detach()
+        {
+            return new F2mPoint(null, AffineXCoord, AffineYCoord, false);
+        }
+
+        public override ECFieldElement YCoord
+        {
+            get
+            {
+                int coord = this.CurveCoordinateSystem;
+
+                switch (coord)
+                {
+                    case ECCurve.COORD_LAMBDA_AFFINE:
+                    case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                    {
+                        ECFieldElement X = RawXCoord, L = RawYCoord;
+
+                        if (this.IsInfinity || X.IsZero)
+                            return L;
+
+                        // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+                        ECFieldElement Y = L.Add(X).Multiply(X);
+                        if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord)
+                        {
+                            ECFieldElement Z = RawZCoords[0];
+                            if (!Z.IsOne)
+                            {
+                                Y = Y.Divide(Z);
+                            }
+                        }
+                        return Y;
+                    }
+                    default:
+                    {
+                        return RawYCoord;
+                    }
+                }
+            }
+        }
+
+        protected internal override bool CompressionYTilde
+        {
+            get
+            {
+                ECFieldElement X = this.RawXCoord;
+                if (X.IsZero)
+                {
+                    return false;
+                }
+
+                ECFieldElement Y = this.RawYCoord;
+
+                switch (this.CurveCoordinateSystem)
+                {
+                    case ECCurve.COORD_LAMBDA_AFFINE:
+                    case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                    {
+                        // Y is actually Lambda (X + Y/X) here
+                        return Y.TestBitZero() != X.TestBitZero();
+                    }
+                    default:
+                    {
+                        return Y.Divide(X).TestBitZero();
+                    }
+                }
+            }
+        }
+
+        public override ECPoint Add(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+            int coord = curve.CoordinateSystem;
+
+            ECFieldElement X1 = this.RawXCoord;
+            ECFieldElement X2 = b.RawXCoord;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                {
+                    ECFieldElement Y1 = this.RawYCoord;
+                    ECFieldElement Y2 = b.RawYCoord;
+
+                    ECFieldElement dx = X1.Add(X2), dy = Y1.Add(Y2);
+                    if (dx.IsZero)
+                    {
+                        if (dy.IsZero)
+                        {
+                            return Twice();
+                        }
+
+                        return curve.Infinity;
+                    }
+
+                    ECFieldElement L = dy.Divide(dx);
+
+                    ECFieldElement X3 = L.Square().Add(L).Add(dx).Add(curve.A);
+                    ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1);
+
+                    return new F2mPoint(curve, X3, Y3, IsCompressed);
+                }
+                case ECCurve.COORD_HOMOGENEOUS:
+                {
+                    ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0];
+                    ECFieldElement Y2 = b.RawYCoord, Z2 = b.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+                    ECFieldElement U1 = Y2, V1 = X2;
+                    if (!Z1IsOne)
+                    {
+                        U1 = U1.Multiply(Z1);
+                        V1 = V1.Multiply(Z1);
+                    }
+
+                    bool Z2IsOne = Z2.IsOne;
+                    ECFieldElement U2 = Y1, V2 = X1;
+                    if (!Z2IsOne)
+                    {
+                        U2 = U2.Multiply(Z2);
+                        V2 = V2.Multiply(Z2);
+                    }
+
+                    ECFieldElement U = U1.Add(U2);
+                    ECFieldElement V = V1.Add(V2);
+
+                    if (V.IsZero)
+                    {
+                        if (U.IsZero)
+                        {
+                            return Twice();
+                        }
+
+                        return curve.Infinity;
+                    }
+
+                    ECFieldElement VSq = V.Square();
+                    ECFieldElement VCu = VSq.Multiply(V);
+                    ECFieldElement W = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.Multiply(Z2);
+                    ECFieldElement uv = U.Add(V);
+                    ECFieldElement A = uv.MultiplyPlusProduct(U, VSq, curve.A).Multiply(W).Add(VCu);
+
+                    ECFieldElement X3 = V.Multiply(A);
+                    ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.Multiply(Z2);
+                    ECFieldElement Y3 = U.MultiplyPlusProduct(X1, V, Y1).MultiplyPlusProduct(VSqZ2, uv, A);
+                    ECFieldElement Z3 = VCu.Multiply(W);
+
+                    return new F2mPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+                case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                {
+                    if (X1.IsZero)
+                    {
+                        if (X2.IsZero)
+                            return curve.Infinity;
+
+                        return b.Add(this);
+                    }
+
+                    ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0];
+                    ECFieldElement L2 = b.RawYCoord, Z2 = b.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+                    ECFieldElement U2 = X2, S2 = L2;
+                    if (!Z1IsOne)
+                    {
+                        U2 = U2.Multiply(Z1);
+                        S2 = S2.Multiply(Z1);
+                    }
+
+                    bool Z2IsOne = Z2.IsOne;
+                    ECFieldElement U1 = X1, S1 = L1;
+                    if (!Z2IsOne)
+                    {
+                        U1 = U1.Multiply(Z2);
+                        S1 = S1.Multiply(Z2);
+                    }
+
+                    ECFieldElement A = S1.Add(S2);
+                    ECFieldElement B = U1.Add(U2);
+
+                    if (B.IsZero)
+                    {
+                        if (A.IsZero)
+                        {
+                            return Twice();
+                        }
+
+                        return curve.Infinity;
+                    }
+
+                    ECFieldElement X3, L3, Z3;
+                    if (X2.IsZero)
+                    {
+                        // TODO This can probably be optimized quite a bit
+                        ECPoint p = this.Normalize();
+                        X1 = p.RawXCoord;
+                        ECFieldElement Y1 = p.YCoord;
+
+                        ECFieldElement Y2 = L2;
+                        ECFieldElement L = Y1.Add(Y2).Divide(X1);
+
+                        X3 = L.Square().Add(L).Add(X1).Add(curve.A);
+                        if (X3.IsZero)
+                        {
+                            return new F2mPoint(curve, X3, curve.B.Sqrt(), IsCompressed);
+                        }
+
+                        ECFieldElement Y3 = L.Multiply(X1.Add(X3)).Add(X3).Add(Y1);
+                        L3 = Y3.Divide(X3).Add(X3);
+                        Z3 = curve.FromBigInteger(BigInteger.One);
+                    }
+                    else
+                    {
+                        B = B.Square();
+
+                        ECFieldElement AU1 = A.Multiply(U1);
+                        ECFieldElement AU2 = A.Multiply(U2);
+
+                        X3 = AU1.Multiply(AU2);
+                        if (X3.IsZero)
+                        {
+                            return new F2mPoint(curve, X3, curve.B.Sqrt(), IsCompressed);
+                        }
+
+                        ECFieldElement ABZ2 = A.Multiply(B);
+                        if (!Z2IsOne)
+                        {
+                            ABZ2 = ABZ2.Multiply(Z2);
+                        }
+
+                        L3 = AU2.Add(B).SquarePlusProduct(ABZ2, L1.Add(Z1));
+
+                        Z3 = ABZ2;
+                        if (!Z1IsOne)
+                        {
+                            Z3 = Z3.Multiply(Z1);
+                        }
+                    }
+
+                    return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+                default:
+                {
+                    throw new InvalidOperationException("unsupported coordinate system");
+                }
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see Org.BouncyCastle.Math.EC.ECPoint#twice()
+         */
+        public override ECPoint Twice()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECCurve curve = this.Curve;
+
+            ECFieldElement X1 = this.RawXCoord;
+            if (X1.IsZero)
+            {
+                // A point with X == 0 is it's own additive inverse
+                return curve.Infinity;
+            }
+
+            int coord = curve.CoordinateSystem;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                {
+                    ECFieldElement Y1 = this.RawYCoord;
+
+                    ECFieldElement L1 = Y1.Divide(X1).Add(X1);
+
+                    ECFieldElement X3 = L1.Square().Add(L1).Add(curve.A);
+                    ECFieldElement Y3 = X1.SquarePlusProduct(X3, L1.AddOne());
+
+                    return new F2mPoint(curve, X3, Y3, IsCompressed);
+                }
+                case ECCurve.COORD_HOMOGENEOUS:
+                {
+                    ECFieldElement Y1 = this.RawYCoord, Z1 = this.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+                    ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1);
+                    ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.Multiply(Z1);
+
+                    ECFieldElement X1Sq = X1.Square();
+                    ECFieldElement S = X1Sq.Add(Y1Z1);
+                    ECFieldElement V = X1Z1;
+                    ECFieldElement vSquared = V.Square();
+                    ECFieldElement sv = S.Add(V);
+                    ECFieldElement h = sv.MultiplyPlusProduct(S, vSquared, curve.A);
+
+                    ECFieldElement X3 = V.Multiply(h);
+                    ECFieldElement Y3 = X1Sq.Square().MultiplyPlusProduct(V, h, sv);
+                    ECFieldElement Z3 = V.Multiply(vSquared);
+
+                    return new F2mPoint(curve, X3, Y3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+                case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                {
+                    ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0];
+
+                    bool Z1IsOne = Z1.IsOne;
+                    ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.Multiply(Z1);
+                    ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.Square();
+                    ECFieldElement a = curve.A;
+                    ECFieldElement aZ1Sq = Z1IsOne ? a : a.Multiply(Z1Sq);
+                    ECFieldElement T = L1.Square().Add(L1Z1).Add(aZ1Sq);
+                    if (T.IsZero)
+                    {
+                        return new F2mPoint(curve, T, curve.B.Sqrt(), IsCompressed);
+                    }
+
+                    ECFieldElement X3 = T.Square();
+                    ECFieldElement Z3 = Z1IsOne ? T : T.Multiply(Z1Sq);
+
+                    ECFieldElement b = curve.B;
+                    ECFieldElement L3;
+                    if (b.BitLength < (curve.FieldSize >> 1))
+                    {
+                        ECFieldElement t1 = L1.Add(X1).Square();
+                        ECFieldElement t2;
+                        if (b.IsOne)
+                        {
+                            t2 = aZ1Sq.Add(Z1Sq).Square();
+                        }
+                        else
+                        {
+                            // TODO Can be calculated with one square if we pre-compute sqrt(b)
+                            t2 = aZ1Sq.SquarePlusProduct(b, Z1Sq.Square());
+                        }
+                        L3 = t1.Add(T).Add(Z1Sq).Multiply(t1).Add(t2).Add(X3);
+                        if (a.IsZero)
+                        {
+                            L3 = L3.Add(Z3);
+                        }
+                        else if (!a.IsOne)
+                        {
+                            L3 = L3.Add(a.AddOne().Multiply(Z3));
+                        }
+                    }
+                    else
+                    {
+                        ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.Multiply(Z1);
+                        L3 = X1Z1.SquarePlusProduct(T, L1Z1).Add(X3).Add(Z3);
+                    }
+
+                    return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+                default:
+                {
+                    throw new InvalidOperationException("unsupported coordinate system");
+                }
+            }
+        }
+
+        public override ECPoint TwicePlus(ECPoint b)
+        {
+            if (this.IsInfinity)
+                return b;
+            if (b.IsInfinity)
+                return Twice();
+
+            ECCurve curve = this.Curve;
+
+            ECFieldElement X1 = this.RawXCoord;
+            if (X1.IsZero)
+            {
+                // A point with X == 0 is it's own additive inverse
+                return b;
+            }
+
+            int coord = curve.CoordinateSystem;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                {
+                    // NOTE: twicePlus() only optimized for lambda-affine argument
+                    ECFieldElement X2 = b.RawXCoord, Z2 = b.RawZCoords[0];
+                    if (X2.IsZero || !Z2.IsOne)
+                    {
+                        return Twice().Add(b);
+                    }
+
+                    ECFieldElement L1 = this.RawYCoord, Z1 = this.RawZCoords[0];
+                    ECFieldElement L2 = b.RawYCoord;
+
+                    ECFieldElement X1Sq = X1.Square();
+                    ECFieldElement L1Sq = L1.Square();
+                    ECFieldElement Z1Sq = Z1.Square();
+                    ECFieldElement L1Z1 = L1.Multiply(Z1);
+
+                    ECFieldElement T = curve.A.Multiply(Z1Sq).Add(L1Sq).Add(L1Z1);
+                    ECFieldElement L2plus1 = L2.AddOne();
+                    ECFieldElement A = curve.A.Add(L2plus1).Multiply(Z1Sq).Add(L1Sq).MultiplyPlusProduct(T, X1Sq, Z1Sq);
+                    ECFieldElement X2Z1Sq = X2.Multiply(Z1Sq);
+                    ECFieldElement B = X2Z1Sq.Add(T).Square();
+
+                    if (B.IsZero)
+                    {
+                        if (A.IsZero)
+                        {
+                            return b.Twice();
+                        }
+
+                        return curve.Infinity;
+                    }
+
+                    if (A.IsZero)
+                    {
+                        return new F2mPoint(curve, A, curve.B.Sqrt(), IsCompressed);
+                    }
+
+                    ECFieldElement X3 = A.Square().Multiply(X2Z1Sq);
+                    ECFieldElement Z3 = A.Multiply(B).Multiply(Z1Sq);
+                    ECFieldElement L3 = A.Add(B).Square().MultiplyPlusProduct(T, L2plus1, Z3);
+
+                    return new F2mPoint(curve, X3, L3, new ECFieldElement[] { Z3 }, IsCompressed);
+                }
+                default:
+                {
+                    return Twice().Add(b);
+                }
+            }
+        }
+
+        public override ECPoint Negate()
+        {
+            if (this.IsInfinity)
+                return this;
+
+            ECFieldElement X = this.RawXCoord;
+            if (X.IsZero)
+                return this;
+
+            ECCurve curve = this.Curve;
+            int coord = curve.CoordinateSystem;
+
+            switch (coord)
+            {
+                case ECCurve.COORD_AFFINE:
+                {
+                    ECFieldElement Y = this.RawYCoord;
+                    return new F2mPoint(curve, X, Y.Add(X), IsCompressed);
+                }
+                case ECCurve.COORD_HOMOGENEOUS:
+                {
+                    ECFieldElement Y = this.RawYCoord, Z = this.RawZCoords[0];
+                    return new F2mPoint(curve, X, Y.Add(X), new ECFieldElement[] { Z }, IsCompressed);
+                }
+                case ECCurve.COORD_LAMBDA_AFFINE:
+                {
+                    ECFieldElement L = this.RawYCoord;
+                    return new F2mPoint(curve, X, L.AddOne(), IsCompressed);
+                }
+                case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                {
+                    // L is actually Lambda (X + Y/X) here
+                    ECFieldElement L = this.RawYCoord, Z = this.RawZCoords[0];
+                    return new F2mPoint(curve, X, L.Add(Z), new ECFieldElement[] { Z }, IsCompressed);
+                }
+                default:
+                {
+                    throw new InvalidOperationException("unsupported coordinate system");
+                }
+            }
+        }
+    }
+}

+ 9 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPointMap.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC
+{
+    internal interface ECPointMap
+    {
+        ECPoint Map(ECPoint p);
+    }
+}

+ 2206 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/LongArray.cs

@@ -0,0 +1,2206 @@
+using System;
+using System.Text;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC
+{
+    internal class LongArray
+    {
+        //private static long DEInterleave_MASK = 0x5555555555555555L;
+
+        /*
+         * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits.
+         * In a binary field, this operation is the same as squaring an 8 bit number.
+         */
+        private static readonly ushort[] INTERLEAVE2_TABLE = new ushort[]
+        {
+            0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
+            0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
+            0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
+            0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
+            0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
+            0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
+            0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
+            0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
+            0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
+            0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
+            0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
+            0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
+            0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
+            0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
+            0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
+            0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
+            0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
+            0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
+            0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
+            0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
+            0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
+            0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
+            0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
+            0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
+            0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
+            0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
+            0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
+            0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
+            0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
+            0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
+            0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
+            0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555
+        };
+
+        /*
+         * This expands 7 bit indices into 21 bit contents (high bit 18), by inserting 0s between bits.
+         */
+        private static readonly int[] INTERLEAVE3_TABLE = new  int[]
+        {
+            0x00000, 0x00001, 0x00008, 0x00009, 0x00040, 0x00041, 0x00048, 0x00049,
+            0x00200, 0x00201, 0x00208, 0x00209, 0x00240, 0x00241, 0x00248, 0x00249,
+            0x01000, 0x01001, 0x01008, 0x01009, 0x01040, 0x01041, 0x01048, 0x01049,
+            0x01200, 0x01201, 0x01208, 0x01209, 0x01240, 0x01241, 0x01248, 0x01249,
+            0x08000, 0x08001, 0x08008, 0x08009, 0x08040, 0x08041, 0x08048, 0x08049,
+            0x08200, 0x08201, 0x08208, 0x08209, 0x08240, 0x08241, 0x08248, 0x08249,
+            0x09000, 0x09001, 0x09008, 0x09009, 0x09040, 0x09041, 0x09048, 0x09049,
+            0x09200, 0x09201, 0x09208, 0x09209, 0x09240, 0x09241, 0x09248, 0x09249,
+            0x40000, 0x40001, 0x40008, 0x40009, 0x40040, 0x40041, 0x40048, 0x40049,
+            0x40200, 0x40201, 0x40208, 0x40209, 0x40240, 0x40241, 0x40248, 0x40249,
+            0x41000, 0x41001, 0x41008, 0x41009, 0x41040, 0x41041, 0x41048, 0x41049,
+            0x41200, 0x41201, 0x41208, 0x41209, 0x41240, 0x41241, 0x41248, 0x41249,
+            0x48000, 0x48001, 0x48008, 0x48009, 0x48040, 0x48041, 0x48048, 0x48049,
+            0x48200, 0x48201, 0x48208, 0x48209, 0x48240, 0x48241, 0x48248, 0x48249,
+            0x49000, 0x49001, 0x49008, 0x49009, 0x49040, 0x49041, 0x49048, 0x49049,
+            0x49200, 0x49201, 0x49208, 0x49209, 0x49240, 0x49241, 0x49248, 0x49249
+        };
+
+        /*
+         * This expands 8 bit indices into 32 bit contents (high bit 28), by inserting 0s between bits.
+         */
+        private static readonly int[] INTERLEAVE4_TABLE = new int[]
+        {
+            0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, 0x00000101, 0x00000110, 0x00000111,
+            0x00001000, 0x00001001, 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110, 0x00001111,
+            0x00010000, 0x00010001, 0x00010010, 0x00010011, 0x00010100, 0x00010101, 0x00010110, 0x00010111,
+            0x00011000, 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101, 0x00011110, 0x00011111,
+            0x00100000, 0x00100001, 0x00100010, 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111,
+            0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100, 0x00101101, 0x00101110, 0x00101111,
+            0x00110000, 0x00110001, 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110, 0x00110111,
+            0x00111000, 0x00111001, 0x00111010, 0x00111011, 0x00111100, 0x00111101, 0x00111110, 0x00111111,
+            0x01000000, 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101, 0x01000110, 0x01000111,
+            0x01001000, 0x01001001, 0x01001010, 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111,
+            0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100, 0x01010101, 0x01010110, 0x01010111,
+            0x01011000, 0x01011001, 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110, 0x01011111,
+            0x01100000, 0x01100001, 0x01100010, 0x01100011, 0x01100100, 0x01100101, 0x01100110, 0x01100111,
+            0x01101000, 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101, 0x01101110, 0x01101111,
+            0x01110000, 0x01110001, 0x01110010, 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111,
+            0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100, 0x01111101, 0x01111110, 0x01111111,
+            0x10000000, 0x10000001, 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110, 0x10000111,
+            0x10001000, 0x10001001, 0x10001010, 0x10001011, 0x10001100, 0x10001101, 0x10001110, 0x10001111,
+            0x10010000, 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101, 0x10010110, 0x10010111,
+            0x10011000, 0x10011001, 0x10011010, 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111,
+            0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100, 0x10100101, 0x10100110, 0x10100111,
+            0x10101000, 0x10101001, 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110, 0x10101111,
+            0x10110000, 0x10110001, 0x10110010, 0x10110011, 0x10110100, 0x10110101, 0x10110110, 0x10110111,
+            0x10111000, 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101, 0x10111110, 0x10111111,
+            0x11000000, 0x11000001, 0x11000010, 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111,
+            0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100, 0x11001101, 0x11001110, 0x11001111,
+            0x11010000, 0x11010001, 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110, 0x11010111,
+            0x11011000, 0x11011001, 0x11011010, 0x11011011, 0x11011100, 0x11011101, 0x11011110, 0x11011111,
+            0x11100000, 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101, 0x11100110, 0x11100111,
+            0x11101000, 0x11101001, 0x11101010, 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111,
+            0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100, 0x11110101, 0x11110110, 0x11110111,
+            0x11111000, 0x11111001, 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110, 0x11111111
+        };
+
+        /*
+         * This expands 7 bit indices into 35 bit contents (high bit 30), by inserting 0s between bits.
+         */
+        private static readonly int[] INTERLEAVE5_TABLE = new int[] {
+            0x00000000, 0x00000001, 0x00000020, 0x00000021, 0x00000400, 0x00000401, 0x00000420, 0x00000421,
+            0x00008000, 0x00008001, 0x00008020, 0x00008021, 0x00008400, 0x00008401, 0x00008420, 0x00008421,
+            0x00100000, 0x00100001, 0x00100020, 0x00100021, 0x00100400, 0x00100401, 0x00100420, 0x00100421,
+            0x00108000, 0x00108001, 0x00108020, 0x00108021, 0x00108400, 0x00108401, 0x00108420, 0x00108421,
+            0x02000000, 0x02000001, 0x02000020, 0x02000021, 0x02000400, 0x02000401, 0x02000420, 0x02000421,
+            0x02008000, 0x02008001, 0x02008020, 0x02008021, 0x02008400, 0x02008401, 0x02008420, 0x02008421,
+            0x02100000, 0x02100001, 0x02100020, 0x02100021, 0x02100400, 0x02100401, 0x02100420, 0x02100421,
+            0x02108000, 0x02108001, 0x02108020, 0x02108021, 0x02108400, 0x02108401, 0x02108420, 0x02108421,
+            0x40000000, 0x40000001, 0x40000020, 0x40000021, 0x40000400, 0x40000401, 0x40000420, 0x40000421,
+            0x40008000, 0x40008001, 0x40008020, 0x40008021, 0x40008400, 0x40008401, 0x40008420, 0x40008421,
+            0x40100000, 0x40100001, 0x40100020, 0x40100021, 0x40100400, 0x40100401, 0x40100420, 0x40100421,
+            0x40108000, 0x40108001, 0x40108020, 0x40108021, 0x40108400, 0x40108401, 0x40108420, 0x40108421,
+            0x42000000, 0x42000001, 0x42000020, 0x42000021, 0x42000400, 0x42000401, 0x42000420, 0x42000421,
+            0x42008000, 0x42008001, 0x42008020, 0x42008021, 0x42008400, 0x42008401, 0x42008420, 0x42008421,
+            0x42100000, 0x42100001, 0x42100020, 0x42100021, 0x42100400, 0x42100401, 0x42100420, 0x42100421,
+            0x42108000, 0x42108001, 0x42108020, 0x42108021, 0x42108400, 0x42108401, 0x42108420, 0x42108421
+        };
+
+        /*
+         * This expands 9 bit indices into 63 bit (long) contents (high bit 56), by inserting 0s between bits.
+         */
+        private static readonly long[] INTERLEAVE7_TABLE = new long[]
+        {
+            0x0000000000000000L, 0x0000000000000001L, 0x0000000000000080L, 0x0000000000000081L,
+            0x0000000000004000L, 0x0000000000004001L, 0x0000000000004080L, 0x0000000000004081L,
+            0x0000000000200000L, 0x0000000000200001L, 0x0000000000200080L, 0x0000000000200081L,
+            0x0000000000204000L, 0x0000000000204001L, 0x0000000000204080L, 0x0000000000204081L,
+            0x0000000010000000L, 0x0000000010000001L, 0x0000000010000080L, 0x0000000010000081L,
+            0x0000000010004000L, 0x0000000010004001L, 0x0000000010004080L, 0x0000000010004081L,
+            0x0000000010200000L, 0x0000000010200001L, 0x0000000010200080L, 0x0000000010200081L,
+            0x0000000010204000L, 0x0000000010204001L, 0x0000000010204080L, 0x0000000010204081L,
+            0x0000000800000000L, 0x0000000800000001L, 0x0000000800000080L, 0x0000000800000081L,
+            0x0000000800004000L, 0x0000000800004001L, 0x0000000800004080L, 0x0000000800004081L,
+            0x0000000800200000L, 0x0000000800200001L, 0x0000000800200080L, 0x0000000800200081L,
+            0x0000000800204000L, 0x0000000800204001L, 0x0000000800204080L, 0x0000000800204081L,
+            0x0000000810000000L, 0x0000000810000001L, 0x0000000810000080L, 0x0000000810000081L,
+            0x0000000810004000L, 0x0000000810004001L, 0x0000000810004080L, 0x0000000810004081L,
+            0x0000000810200000L, 0x0000000810200001L, 0x0000000810200080L, 0x0000000810200081L,
+            0x0000000810204000L, 0x0000000810204001L, 0x0000000810204080L, 0x0000000810204081L,
+            0x0000040000000000L, 0x0000040000000001L, 0x0000040000000080L, 0x0000040000000081L,
+            0x0000040000004000L, 0x0000040000004001L, 0x0000040000004080L, 0x0000040000004081L,
+            0x0000040000200000L, 0x0000040000200001L, 0x0000040000200080L, 0x0000040000200081L,
+            0x0000040000204000L, 0x0000040000204001L, 0x0000040000204080L, 0x0000040000204081L,
+            0x0000040010000000L, 0x0000040010000001L, 0x0000040010000080L, 0x0000040010000081L,
+            0x0000040010004000L, 0x0000040010004001L, 0x0000040010004080L, 0x0000040010004081L,
+            0x0000040010200000L, 0x0000040010200001L, 0x0000040010200080L, 0x0000040010200081L,
+            0x0000040010204000L, 0x0000040010204001L, 0x0000040010204080L, 0x0000040010204081L,
+            0x0000040800000000L, 0x0000040800000001L, 0x0000040800000080L, 0x0000040800000081L,
+            0x0000040800004000L, 0x0000040800004001L, 0x0000040800004080L, 0x0000040800004081L,
+            0x0000040800200000L, 0x0000040800200001L, 0x0000040800200080L, 0x0000040800200081L,
+            0x0000040800204000L, 0x0000040800204001L, 0x0000040800204080L, 0x0000040800204081L,
+            0x0000040810000000L, 0x0000040810000001L, 0x0000040810000080L, 0x0000040810000081L,
+            0x0000040810004000L, 0x0000040810004001L, 0x0000040810004080L, 0x0000040810004081L,
+            0x0000040810200000L, 0x0000040810200001L, 0x0000040810200080L, 0x0000040810200081L,
+            0x0000040810204000L, 0x0000040810204001L, 0x0000040810204080L, 0x0000040810204081L,
+            0x0002000000000000L, 0x0002000000000001L, 0x0002000000000080L, 0x0002000000000081L,
+            0x0002000000004000L, 0x0002000000004001L, 0x0002000000004080L, 0x0002000000004081L,
+            0x0002000000200000L, 0x0002000000200001L, 0x0002000000200080L, 0x0002000000200081L,
+            0x0002000000204000L, 0x0002000000204001L, 0x0002000000204080L, 0x0002000000204081L,
+            0x0002000010000000L, 0x0002000010000001L, 0x0002000010000080L, 0x0002000010000081L,
+            0x0002000010004000L, 0x0002000010004001L, 0x0002000010004080L, 0x0002000010004081L,
+            0x0002000010200000L, 0x0002000010200001L, 0x0002000010200080L, 0x0002000010200081L,
+            0x0002000010204000L, 0x0002000010204001L, 0x0002000010204080L, 0x0002000010204081L,
+            0x0002000800000000L, 0x0002000800000001L, 0x0002000800000080L, 0x0002000800000081L,
+            0x0002000800004000L, 0x0002000800004001L, 0x0002000800004080L, 0x0002000800004081L,
+            0x0002000800200000L, 0x0002000800200001L, 0x0002000800200080L, 0x0002000800200081L,
+            0x0002000800204000L, 0x0002000800204001L, 0x0002000800204080L, 0x0002000800204081L,
+            0x0002000810000000L, 0x0002000810000001L, 0x0002000810000080L, 0x0002000810000081L,
+            0x0002000810004000L, 0x0002000810004001L, 0x0002000810004080L, 0x0002000810004081L,
+            0x0002000810200000L, 0x0002000810200001L, 0x0002000810200080L, 0x0002000810200081L,
+            0x0002000810204000L, 0x0002000810204001L, 0x0002000810204080L, 0x0002000810204081L,
+            0x0002040000000000L, 0x0002040000000001L, 0x0002040000000080L, 0x0002040000000081L,
+            0x0002040000004000L, 0x0002040000004001L, 0x0002040000004080L, 0x0002040000004081L,
+            0x0002040000200000L, 0x0002040000200001L, 0x0002040000200080L, 0x0002040000200081L,
+            0x0002040000204000L, 0x0002040000204001L, 0x0002040000204080L, 0x0002040000204081L,
+            0x0002040010000000L, 0x0002040010000001L, 0x0002040010000080L, 0x0002040010000081L,
+            0x0002040010004000L, 0x0002040010004001L, 0x0002040010004080L, 0x0002040010004081L,
+            0x0002040010200000L, 0x0002040010200001L, 0x0002040010200080L, 0x0002040010200081L,
+            0x0002040010204000L, 0x0002040010204001L, 0x0002040010204080L, 0x0002040010204081L,
+            0x0002040800000000L, 0x0002040800000001L, 0x0002040800000080L, 0x0002040800000081L,
+            0x0002040800004000L, 0x0002040800004001L, 0x0002040800004080L, 0x0002040800004081L,
+            0x0002040800200000L, 0x0002040800200001L, 0x0002040800200080L, 0x0002040800200081L,
+            0x0002040800204000L, 0x0002040800204001L, 0x0002040800204080L, 0x0002040800204081L,
+            0x0002040810000000L, 0x0002040810000001L, 0x0002040810000080L, 0x0002040810000081L,
+            0x0002040810004000L, 0x0002040810004001L, 0x0002040810004080L, 0x0002040810004081L,
+            0x0002040810200000L, 0x0002040810200001L, 0x0002040810200080L, 0x0002040810200081L,
+            0x0002040810204000L, 0x0002040810204001L, 0x0002040810204080L, 0x0002040810204081L,
+            0x0100000000000000L, 0x0100000000000001L, 0x0100000000000080L, 0x0100000000000081L,
+            0x0100000000004000L, 0x0100000000004001L, 0x0100000000004080L, 0x0100000000004081L,
+            0x0100000000200000L, 0x0100000000200001L, 0x0100000000200080L, 0x0100000000200081L,
+            0x0100000000204000L, 0x0100000000204001L, 0x0100000000204080L, 0x0100000000204081L,
+            0x0100000010000000L, 0x0100000010000001L, 0x0100000010000080L, 0x0100000010000081L,
+            0x0100000010004000L, 0x0100000010004001L, 0x0100000010004080L, 0x0100000010004081L,
+            0x0100000010200000L, 0x0100000010200001L, 0x0100000010200080L, 0x0100000010200081L,
+            0x0100000010204000L, 0x0100000010204001L, 0x0100000010204080L, 0x0100000010204081L,
+            0x0100000800000000L, 0x0100000800000001L, 0x0100000800000080L, 0x0100000800000081L,
+            0x0100000800004000L, 0x0100000800004001L, 0x0100000800004080L, 0x0100000800004081L,
+            0x0100000800200000L, 0x0100000800200001L, 0x0100000800200080L, 0x0100000800200081L,
+            0x0100000800204000L, 0x0100000800204001L, 0x0100000800204080L, 0x0100000800204081L,
+            0x0100000810000000L, 0x0100000810000001L, 0x0100000810000080L, 0x0100000810000081L,
+            0x0100000810004000L, 0x0100000810004001L, 0x0100000810004080L, 0x0100000810004081L,
+            0x0100000810200000L, 0x0100000810200001L, 0x0100000810200080L, 0x0100000810200081L,
+            0x0100000810204000L, 0x0100000810204001L, 0x0100000810204080L, 0x0100000810204081L,
+            0x0100040000000000L, 0x0100040000000001L, 0x0100040000000080L, 0x0100040000000081L,
+            0x0100040000004000L, 0x0100040000004001L, 0x0100040000004080L, 0x0100040000004081L,
+            0x0100040000200000L, 0x0100040000200001L, 0x0100040000200080L, 0x0100040000200081L,
+            0x0100040000204000L, 0x0100040000204001L, 0x0100040000204080L, 0x0100040000204081L,
+            0x0100040010000000L, 0x0100040010000001L, 0x0100040010000080L, 0x0100040010000081L,
+            0x0100040010004000L, 0x0100040010004001L, 0x0100040010004080L, 0x0100040010004081L,
+            0x0100040010200000L, 0x0100040010200001L, 0x0100040010200080L, 0x0100040010200081L,
+            0x0100040010204000L, 0x0100040010204001L, 0x0100040010204080L, 0x0100040010204081L,
+            0x0100040800000000L, 0x0100040800000001L, 0x0100040800000080L, 0x0100040800000081L,
+            0x0100040800004000L, 0x0100040800004001L, 0x0100040800004080L, 0x0100040800004081L,
+            0x0100040800200000L, 0x0100040800200001L, 0x0100040800200080L, 0x0100040800200081L,
+            0x0100040800204000L, 0x0100040800204001L, 0x0100040800204080L, 0x0100040800204081L,
+            0x0100040810000000L, 0x0100040810000001L, 0x0100040810000080L, 0x0100040810000081L,
+            0x0100040810004000L, 0x0100040810004001L, 0x0100040810004080L, 0x0100040810004081L,
+            0x0100040810200000L, 0x0100040810200001L, 0x0100040810200080L, 0x0100040810200081L,
+            0x0100040810204000L, 0x0100040810204001L, 0x0100040810204080L, 0x0100040810204081L,
+            0x0102000000000000L, 0x0102000000000001L, 0x0102000000000080L, 0x0102000000000081L,
+            0x0102000000004000L, 0x0102000000004001L, 0x0102000000004080L, 0x0102000000004081L,
+            0x0102000000200000L, 0x0102000000200001L, 0x0102000000200080L, 0x0102000000200081L,
+            0x0102000000204000L, 0x0102000000204001L, 0x0102000000204080L, 0x0102000000204081L,
+            0x0102000010000000L, 0x0102000010000001L, 0x0102000010000080L, 0x0102000010000081L,
+            0x0102000010004000L, 0x0102000010004001L, 0x0102000010004080L, 0x0102000010004081L,
+            0x0102000010200000L, 0x0102000010200001L, 0x0102000010200080L, 0x0102000010200081L,
+            0x0102000010204000L, 0x0102000010204001L, 0x0102000010204080L, 0x0102000010204081L,
+            0x0102000800000000L, 0x0102000800000001L, 0x0102000800000080L, 0x0102000800000081L,
+            0x0102000800004000L, 0x0102000800004001L, 0x0102000800004080L, 0x0102000800004081L,
+            0x0102000800200000L, 0x0102000800200001L, 0x0102000800200080L, 0x0102000800200081L,
+            0x0102000800204000L, 0x0102000800204001L, 0x0102000800204080L, 0x0102000800204081L,
+            0x0102000810000000L, 0x0102000810000001L, 0x0102000810000080L, 0x0102000810000081L,
+            0x0102000810004000L, 0x0102000810004001L, 0x0102000810004080L, 0x0102000810004081L,
+            0x0102000810200000L, 0x0102000810200001L, 0x0102000810200080L, 0x0102000810200081L,
+            0x0102000810204000L, 0x0102000810204001L, 0x0102000810204080L, 0x0102000810204081L,
+            0x0102040000000000L, 0x0102040000000001L, 0x0102040000000080L, 0x0102040000000081L,
+            0x0102040000004000L, 0x0102040000004001L, 0x0102040000004080L, 0x0102040000004081L,
+            0x0102040000200000L, 0x0102040000200001L, 0x0102040000200080L, 0x0102040000200081L,
+            0x0102040000204000L, 0x0102040000204001L, 0x0102040000204080L, 0x0102040000204081L,
+            0x0102040010000000L, 0x0102040010000001L, 0x0102040010000080L, 0x0102040010000081L,
+            0x0102040010004000L, 0x0102040010004001L, 0x0102040010004080L, 0x0102040010004081L,
+            0x0102040010200000L, 0x0102040010200001L, 0x0102040010200080L, 0x0102040010200081L,
+            0x0102040010204000L, 0x0102040010204001L, 0x0102040010204080L, 0x0102040010204081L,
+            0x0102040800000000L, 0x0102040800000001L, 0x0102040800000080L, 0x0102040800000081L,
+            0x0102040800004000L, 0x0102040800004001L, 0x0102040800004080L, 0x0102040800004081L,
+            0x0102040800200000L, 0x0102040800200001L, 0x0102040800200080L, 0x0102040800200081L,
+            0x0102040800204000L, 0x0102040800204001L, 0x0102040800204080L, 0x0102040800204081L,
+            0x0102040810000000L, 0x0102040810000001L, 0x0102040810000080L, 0x0102040810000081L,
+            0x0102040810004000L, 0x0102040810004001L, 0x0102040810004080L, 0x0102040810004081L,
+            0x0102040810200000L, 0x0102040810200001L, 0x0102040810200080L, 0x0102040810200081L,
+            0x0102040810204000L, 0x0102040810204001L, 0x0102040810204080L, 0x0102040810204081L
+        };
+
+        // For toString(); must have length 64
+        private const string ZEROES = "0000000000000000000000000000000000000000000000000000000000000000";
+
+        internal static readonly 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
+        };
+
+        // TODO make m fixed for the LongArray, and hence compute T once and for all
+
+        private long[] m_ints;
+
+        public LongArray(int intLen)
+        {
+            m_ints = new long[intLen];
+        }
+
+        public LongArray(long[] ints)
+        {
+            m_ints = ints;
+        }
+
+        public LongArray(long[] ints, int off, int len)
+        {
+            if (off == 0 && len == ints.Length)
+            {
+                m_ints = ints;
+            }
+            else
+            {
+                m_ints = new long[len];
+                Array.Copy(ints, off, m_ints, 0, len);
+            }
+        }
+
+        public LongArray(BigInteger bigInt)
+        {
+            if (bigInt == null || bigInt.SignValue < 0)
+            {
+                throw new ArgumentException("invalid F2m field value", "bigInt");
+            }
+
+            if (bigInt.SignValue == 0)
+            {
+                m_ints = new long[] { 0L };
+                return;
+            }
+
+            byte[] barr = bigInt.ToByteArray();
+            int barrLen = barr.Length;
+            int barrStart = 0;
+            if (barr[0] == 0)
+            {
+                // First byte is 0 to enforce highest (=sign) bit is zero.
+                // In this case ignore barr[0].
+                barrLen--;
+                barrStart = 1;
+            }
+            int intLen = (barrLen + 7) / 8;
+            m_ints = new long[intLen];
+
+            int iarrJ = intLen - 1;
+            int rem = barrLen % 8 + barrStart;
+            long temp = 0;
+            int barrI = barrStart;
+            if (barrStart < rem)
+            {
+                for (; barrI < rem; barrI++)
+                {
+                    temp <<= 8;
+                    uint barrBarrI = barr[barrI];
+                    temp |= barrBarrI;
+                }
+                m_ints[iarrJ--] = temp;
+            }
+
+            for (; iarrJ >= 0; iarrJ--)
+            {
+                temp = 0;
+                for (int i = 0; i < 8; i++)
+                {
+                    temp <<= 8;
+                    uint barrBarrI = barr[barrI++];
+                    temp |= barrBarrI;
+                }
+                m_ints[iarrJ] = temp;
+            }
+        }
+
+        internal void CopyTo(long[] z, int zOff)
+        {
+            Array.Copy(m_ints, 0, z, zOff, m_ints.Length);
+        }
+
+        public bool IsOne()
+        {
+            long[] a = m_ints;
+            if (a[0] != 1L)
+            {
+                return false;
+            }
+            for (int i = 1; i < a.Length; ++i)
+            {
+                if (a[i] != 0L)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public bool IsZero()
+        {
+            long[] a = m_ints;
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] != 0L)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public int GetUsedLength()
+        {
+            return GetUsedLengthFrom(m_ints.Length);
+        }
+
+        public int GetUsedLengthFrom(int from)
+        {
+            long[] a = m_ints;
+            from = System.Math.Min(from, a.Length);
+
+            if (from < 1)
+            {
+                return 0;
+            }
+
+            // Check if first element will act as sentinel
+            if (a[0] != 0)
+            {
+                while (a[--from] == 0)
+                {
+                }
+                return from + 1;
+            }
+
+            do
+            {
+                if (a[--from] != 0)
+                {
+                    return from + 1;
+                }
+            }
+            while (from > 0);
+
+            return 0;
+        }
+
+        public int Degree()
+        {
+            int i = m_ints.Length;
+            long w;
+            do
+            {
+                if (i == 0)
+                {
+                    return 0;
+                }
+                w = m_ints[--i];
+            }
+            while (w == 0);
+
+            return (i << 6) + BitLength(w);
+        }
+
+        private int DegreeFrom(int limit)
+        {
+            int i = (int)(((uint)limit + 62) >> 6);
+            long w;
+            do
+            {
+                if (i == 0)
+                {
+                    return 0;
+                }
+                w = m_ints[--i];
+            }
+            while (w == 0);
+
+            return (i << 6) + BitLength(w);
+        }
+
+    //    private int lowestCoefficient()
+    //    {
+    //        for (int i = 0; i < m_ints.Length; ++i)
+    //        {
+    //            long mi = m_ints[i];
+    //            if (mi != 0)
+    //            {
+    //                int j = 0;
+    //                while ((mi & 0xFFL) == 0)
+    //                {
+    //                    j += 8;
+    //                    mi >>>= 8;
+    //                }
+    //                while ((mi & 1L) == 0)
+    //                {
+    //                    ++j;
+    //                    mi >>>= 1;
+    //                }
+    //                return (i << 6) + j;
+    //            }
+    //        }
+    //        return -1;
+    //    }
+
+        private static int BitLength(long w)
+        {
+            int u = (int)((ulong)w >> 32), b;
+            if (u == 0)
+            {
+                u = (int)w;
+                b = 0;
+            }
+            else
+            {
+                b = 32;
+            }
+
+            int t = (int)((uint)u >> 16), k;
+            if (t == 0)
+            {
+                t = (int)((uint)u >> 8);
+                k = (t == 0) ? BitLengths[u] : 8 + BitLengths[t];
+            }
+            else
+            {
+                int v = (int)((uint)t >> 8);
+                k = (v == 0) ? 16 + BitLengths[t] : 24 + BitLengths[v];
+            }
+
+            return b + k;
+        }
+
+        private long[] ResizedInts(int newLen)
+        {
+            long[] newInts = new long[newLen];
+            Array.Copy(m_ints, 0, newInts, 0, System.Math.Min(m_ints.Length, newLen));
+            return newInts;
+        }
+
+        public BigInteger ToBigInteger()
+        {
+            int usedLen = GetUsedLength();
+            if (usedLen == 0)
+            {
+                return BigInteger.Zero;
+            }
+
+            long highestInt = m_ints[usedLen - 1];
+            byte[] temp = new byte[8];
+            int barrI = 0;
+            bool trailingZeroBytesDone = false;
+            for (int j = 7; j >= 0; j--)
+            {
+                byte thisByte = (byte)((ulong)highestInt >> (8 * j));
+                if (trailingZeroBytesDone || (thisByte != 0))
+                {
+                    trailingZeroBytesDone = true;
+                    temp[barrI++] = thisByte;
+                }
+            }
+
+            int barrLen = 8 * (usedLen - 1) + barrI;
+            byte[] barr = new byte[barrLen];
+            for (int j = 0; j < barrI; j++)
+            {
+                barr[j] = temp[j];
+            }
+            // Highest value int is done now
+
+            for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+            {
+                long mi = m_ints[iarrJ];
+                for (int j = 7; j >= 0; j--)
+                {
+                    barr[barrI++] = (byte)((ulong)mi >> (8 * j));
+                }
+            }
+            return new BigInteger(1, barr);
+        }
+
+    //    private static long shiftUp(long[] x, int xOff, int count)
+    //    {
+    //        long prev = 0;
+    //        for (int i = 0; i < count; ++i)
+    //        {
+    //            long next = x[xOff + i];
+    //            x[xOff + i] = (next << 1) | prev;
+    //            prev = next >>> 63;
+    //        }
+    //        return prev;
+    //    }
+
+        private static long ShiftUp(long[] x, int xOff, int count, int shift)
+        {
+            int shiftInv = 64 - shift;
+            long prev = 0;
+            for (int i = 0; i < count; ++i)
+            {
+                long next = x[xOff + i];
+                x[xOff + i] = (next << shift) | prev;
+                prev = (long)((ulong)next >> shiftInv);
+            }
+            return prev;
+        }
+
+        private static long ShiftUp(long[] x, int xOff, long[] z, int zOff, int count, int shift)
+        {
+            int shiftInv = 64 - shift;
+            long prev = 0;
+            for (int i = 0; i < count; ++i)
+            {
+                long next = x[xOff + i];
+                z[zOff + i] = (next << shift) | prev;
+                prev = (long)((ulong)next >> shiftInv);
+            }
+            return prev;
+        }
+
+        public LongArray AddOne()
+        {
+            if (m_ints.Length == 0)
+            {
+                return new LongArray(new long[]{ 1L });
+            }
+
+            int resultLen = System.Math.Max(1, GetUsedLength());
+            long[] ints = ResizedInts(resultLen);
+            ints[0] ^= 1L;
+            return new LongArray(ints);
+        }
+
+    //    private void addShiftedByBits(LongArray other, int bits)
+    //    {
+    //        int words = bits >>> 6;
+    //        int shift = bits & 0x3F;
+    //
+    //        if (shift == 0)
+    //        {
+    //            addShiftedByWords(other, words);
+    //            return;
+    //        }
+    //
+    //        int otherUsedLen = other.GetUsedLength();
+    //        if (otherUsedLen == 0)
+    //        {
+    //            return;
+    //        }
+    //
+    //        int minLen = otherUsedLen + words + 1;
+    //        if (minLen > m_ints.Length)
+    //        {
+    //            m_ints = resizedInts(minLen);
+    //        }
+    //
+    //        long carry = addShiftedByBits(m_ints, words, other.m_ints, 0, otherUsedLen, shift);
+    //        m_ints[otherUsedLen + words] ^= carry;
+    //    }
+
+        private void AddShiftedByBitsSafe(LongArray other, int otherDegree, int bits)
+        {
+            int otherLen = (int)((uint)(otherDegree + 63) >> 6);
+
+            int words = (int)((uint)bits >> 6);
+            int shift = bits & 0x3F;
+
+            if (shift == 0)
+            {
+                Add(m_ints, words, other.m_ints, 0, otherLen);
+                return;
+            }
+
+            long carry = AddShiftedUp(m_ints, words, other.m_ints, 0, otherLen, shift);
+            if (carry != 0L)
+            {
+                m_ints[otherLen + words] ^= carry;
+            }
+        }
+
+        private static long AddShiftedUp(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+        {
+            int shiftInv = 64 - shift;
+            long prev = 0;
+            for (int i = 0; i < count; ++i)
+            {
+                long next = y[yOff + i];
+                x[xOff + i] ^= (next << shift) | prev;
+                prev = (long)((ulong)next >> shiftInv);
+            }
+            return prev;
+        }
+
+        private static long AddShiftedDown(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+        {
+            int shiftInv = 64 - shift;
+            long prev = 0;
+            int i = count;
+            while (--i >= 0)
+            {
+                long next = y[yOff + i];
+                x[xOff + i] ^= (long)((ulong)next >> shift) | prev;
+                prev = next << shiftInv;
+            }
+            return prev;
+        }
+
+        public void AddShiftedByWords(LongArray other, int words)
+        {
+            int otherUsedLen = other.GetUsedLength();
+            if (otherUsedLen == 0)
+            {
+                return;
+            }
+
+            int minLen = otherUsedLen + words;
+            if (minLen > m_ints.Length)
+            {
+                m_ints = ResizedInts(minLen);
+            }
+
+            Add(m_ints, words, other.m_ints, 0, otherUsedLen);
+        }
+
+        private static void Add(long[] x, int xOff, long[] y, int yOff, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                x[xOff + i] ^= y[yOff + i];
+            }
+        }
+
+        private static void Add(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                z[zOff + i] = x[xOff + i] ^ y[yOff + i];
+            }
+        }
+
+        private static void AddBoth(long[] x, int xOff, long[] y1, int y1Off, long[] y2, int y2Off, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                x[xOff + i] ^= y1[y1Off + i] ^ y2[y2Off + i];
+            }
+        }
+
+        private static void Distribute(long[] x, int src, int dst1, int dst2, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                long v = x[src + i];
+                x[dst1 + i] ^= v;
+                x[dst2 + i] ^= v;
+            }
+        }
+
+        public int Length
+        {
+            get { return m_ints.Length; }
+        }
+
+        private static void FlipWord(long[] buf, int off, int bit, long word)
+        {
+            int n = off + (int)((uint)bit >> 6);
+            int shift = bit & 0x3F;
+            if (shift == 0)
+            {
+                buf[n] ^= word;
+            }
+            else
+            {
+                buf[n] ^= word << shift;
+                word = (long)((ulong)word >> (64 - shift));
+                if (word != 0)
+                {
+                    buf[++n] ^= word;
+                }
+            }
+        }
+
+    //    private static long getWord(long[] buf, int off, int len, int bit)
+    //    {
+    //        int n = off + (bit >>> 6);
+    //        int shift = bit & 0x3F;
+    //        if (shift == 0)
+    //        {
+    //            return buf[n];
+    //        }
+    //        long result = buf[n] >>> shift;
+    //        if (++n < len)
+    //        {
+    //            result |= buf[n] << (64 - shift);
+    //        }
+    //        return result;
+    //    }
+
+        public bool TestBitZero()
+        {
+            return m_ints.Length > 0 && (m_ints[0] & 1L) != 0;
+        }
+
+        private static bool TestBit(long[] buf, int off, int n)
+        {
+            // theInt = n / 64
+            int theInt = (int)((uint)n >> 6);
+            // theBit = n % 64
+            int theBit = n & 0x3F;
+            long tester = 1L << theBit;
+            return (buf[off + theInt] & tester) != 0;
+        }
+
+        private static void FlipBit(long[] buf, int off, int n)
+        {
+            // theInt = n / 64
+            int theInt = (int)((uint)n >> 6);
+            // theBit = n % 64
+            int theBit = n & 0x3F;
+            long flipper = 1L << theBit;
+            buf[off + theInt] ^= flipper;
+        }
+
+    //    private static void SetBit(long[] buf, int off, int n)
+    //    {
+    //        // theInt = n / 64
+    //        int theInt = n >>> 6;
+    //        // theBit = n % 64
+    //        int theBit = n & 0x3F;
+    //        long setter = 1L << theBit;
+    //        buf[off + theInt] |= setter;
+    //    }
+    //
+    //    private static void ClearBit(long[] buf, int off, int n)
+    //    {
+    //        // theInt = n / 64
+    //        int theInt = n >>> 6;
+    //        // theBit = n % 64
+    //        int theBit = n & 0x3F;
+    //        long setter = 1L << theBit;
+    //        buf[off + theInt] &= ~setter;
+    //    }
+
+        private static void MultiplyWord(long a, long[] b, int bLen, long[] c, int cOff)
+        {
+            if ((a & 1L) != 0L)
+            {
+                Add(c, cOff, b, 0, bLen);
+            }
+            int k = 1;
+            while ((a = (long)((ulong)a >> 1)) != 0L)
+            {
+                if ((a & 1L) != 0L)
+                {
+                    long carry = AddShiftedUp(c, cOff, b, 0, bLen, k);
+                    if (carry != 0L)
+                    {
+                        c[cOff + bLen] ^= carry;
+                    }
+                }
+                ++k;
+            }
+        }
+
+        public LongArray ModMultiplyLD(LongArray other, int m, int[] ks)
+        {
+            /*
+             * Find out the degree of each argument and handle the zero cases
+             */
+            int aDeg = Degree();
+            if (aDeg == 0)
+            {
+                return this;
+            }
+            int bDeg = other.Degree();
+            if (bDeg == 0)
+            {
+                return other;
+            }
+
+            /*
+             * Swap if necessary so that A is the smaller argument
+             */
+            LongArray A = this, B = other;
+            if (aDeg > bDeg)
+            {
+                A = other; B = this;
+                int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+            }
+
+            /*
+             * Establish the word lengths of the arguments and result
+             */
+            int aLen = (int)((uint)(aDeg + 63) >> 6);
+            int bLen = (int)((uint)(bDeg + 63) >> 6);
+            int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6);
+
+            if (aLen == 1)
+            {
+                long a0 = A.m_ints[0];
+                if (a0 == 1L)
+                {
+                    return B;
+                }
+
+                /*
+                 * Fast path for small A, with performance dependent only on the number of set bits
+                 */
+                long[] c0 = new long[cLen];
+                MultiplyWord(a0, B.m_ints, bLen, c0, 0);
+
+                /*
+                 * Reduce the raw answer against the reduction coefficients
+                 */
+                return ReduceResult(c0, 0, cLen, m, ks);
+            }
+
+            /*
+             * Determine if B will get bigger during shifting
+             */
+            int bMax = (int)((uint)(bDeg + 7 + 63) >> 6);
+
+            /*
+             * Lookup table for the offset of each B in the tables
+             */
+            int[] ti = new int[16];
+
+            /*
+             * Precompute table of all 4-bit products of B
+             */
+            long[] T0 = new long[bMax << 4];
+            int tOff = bMax;
+            ti[1] = tOff;
+            Array.Copy(B.m_ints, 0, T0, tOff, bLen);
+            for (int i = 2; i < 16; ++i)
+            {
+                ti[i] = (tOff += bMax);
+                if ((i & 1) == 0)
+                {
+                    ShiftUp(T0, (int)((uint)tOff >> 1), T0, tOff, bMax, 1);
+                }
+                else
+                {
+                    Add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax);
+                }
+            }
+
+            /*
+             * Second table with all 4-bit products of B shifted 4 bits
+             */
+            long[] T1 = new long[T0.Length];
+            ShiftUp(T0, 0, T1, 0, T0.Length, 4);
+    //        shiftUp(T0, bMax, T1, bMax, tOff, 4);
+
+            long[] a = A.m_ints;
+            long[] c = new long[cLen];
+
+            int MASK = 0xF;
+
+            /*
+             * Lopez-Dahab algorithm
+             */
+
+            for (int k = 56; k >= 0; k -= 8)
+            {
+                for (int j = 1; j < aLen; j += 2)
+                {
+                    int aVal = (int)((ulong)a[j] >> k);
+                    int u = aVal & MASK;
+                    int v = (int)((uint)aVal >> 4) & MASK;
+                    AddBoth(c, j - 1, T0, ti[u], T1, ti[v], bMax);
+                }
+                ShiftUp(c, 0, cLen, 8);
+            }
+
+            for (int k = 56; k >= 0; k -= 8)
+            {
+                for (int j = 0; j < aLen; j += 2)
+                {
+                    int aVal = (int)((ulong)a[j] >> k);
+                    int u = aVal & MASK;
+                    int v = (int)((uint)aVal >> 4) & MASK;
+                    AddBoth(c, j, T0, ti[u], T1, ti[v], bMax);
+                }
+                if (k > 0)
+                {
+                    ShiftUp(c, 0, cLen, 8);
+                }
+            }
+
+            /*
+             * Finally the raw answer is collected, reduce it against the reduction coefficients
+             */
+            return ReduceResult(c, 0, cLen, m, ks);
+        }
+
+        public LongArray ModMultiply(LongArray other, int m, int[] ks)
+        {
+            /*
+             * Find out the degree of each argument and handle the zero cases
+             */
+            int aDeg = Degree();
+            if (aDeg == 0)
+            {
+                return this;
+            }
+            int bDeg = other.Degree();
+            if (bDeg == 0)
+            {
+                return other;
+            }
+
+            /*
+             * Swap if necessary so that A is the smaller argument
+             */
+            LongArray A = this, B = other;
+            if (aDeg > bDeg)
+            {
+                A = other; B = this;
+                int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+            }
+
+            /*
+             * Establish the word lengths of the arguments and result
+             */
+            int aLen = (int)((uint)(aDeg + 63) >> 6);
+            int bLen = (int)((uint)(bDeg + 63) >> 6);
+            int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6);
+
+            if (aLen == 1)
+            {
+                long a0 = A.m_ints[0];
+                if (a0 == 1L)
+                {
+                    return B;
+                }
+
+                /*
+                 * Fast path for small A, with performance dependent only on the number of set bits
+                 */
+                long[] c0 = new long[cLen];
+                MultiplyWord(a0, B.m_ints, bLen, c0, 0);
+
+                /*
+                 * Reduce the raw answer against the reduction coefficients
+                 */
+                return ReduceResult(c0, 0, cLen, m, ks);
+            }
+
+            /*
+             * Determine if B will get bigger during shifting
+             */
+            int bMax = (int)((uint)(bDeg + 7 + 63) >> 6);
+
+            /*
+             * Lookup table for the offset of each B in the tables
+             */
+            int[] ti = new int[16];
+
+            /*
+             * Precompute table of all 4-bit products of B
+             */
+            long[] T0 = new long[bMax << 4];
+            int tOff = bMax;
+            ti[1] = tOff;
+            Array.Copy(B.m_ints, 0, T0, tOff, bLen);
+            for (int i = 2; i < 16; ++i)
+            {
+                ti[i] = (tOff += bMax);
+                if ((i & 1) == 0)
+                {
+                    ShiftUp(T0, (int)((uint)tOff >> 1), T0, tOff, bMax, 1);
+                }
+                else
+                {
+                    Add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax);
+                }
+            }
+
+            /*
+             * Second table with all 4-bit products of B shifted 4 bits
+             */
+            long[] T1 = new long[T0.Length];
+            ShiftUp(T0, 0, T1, 0, T0.Length, 4);
+    //        ShiftUp(T0, bMax, T1, bMax, tOff, 4);
+
+            long[] a = A.m_ints;
+            long[] c = new long[cLen << 3];
+
+            int MASK = 0xF;
+
+            /*
+             * Lopez-Dahab (Modified) algorithm
+             */
+
+            for (int aPos = 0; aPos < aLen; ++aPos)
+            {
+                long aVal = a[aPos];
+                int cOff = aPos;
+                for (;;)
+                {
+                    int u = (int)aVal & MASK;
+                    aVal = (long)((ulong)aVal >> 4);
+                    int v = (int)aVal & MASK;
+                    AddBoth(c, cOff, T0, ti[u], T1, ti[v], bMax);
+                    aVal = (long)((ulong)aVal >> 4);
+                    if (aVal == 0L)
+                    {
+                        break;
+                    }
+                    cOff += cLen;
+                }
+            }
+
+            {
+                int cOff = c.Length;
+                while ((cOff -= cLen) != 0)
+                {
+                    AddShiftedUp(c, cOff - cLen, c, cOff, cLen, 8);
+                }
+            }
+
+            /*
+             * Finally the raw answer is collected, reduce it against the reduction coefficients
+             */
+            return ReduceResult(c, 0, cLen, m, ks);
+        }
+
+        public LongArray ModMultiplyAlt(LongArray other, int m, int[] ks)
+        {
+            /*
+             * Find out the degree of each argument and handle the zero cases
+             */
+            int aDeg = Degree();
+            if (aDeg == 0)
+            {
+                return this;
+            }
+            int bDeg = other.Degree();
+            if (bDeg == 0)
+            {
+                return other;
+            }
+
+            /*
+             * Swap if necessary so that A is the smaller argument
+             */
+            LongArray A = this, B = other;
+            if (aDeg > bDeg)
+            {
+                A = other; B = this;
+                int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+            }
+
+            /*
+             * Establish the word lengths of the arguments and result
+             */
+            int aLen = (int)((uint)(aDeg + 63) >> 6);
+            int bLen = (int)((uint)(bDeg + 63) >> 6);
+            int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6);
+
+            if (aLen == 1)
+            {
+                long a0 = A.m_ints[0];
+                if (a0 == 1L)
+                {
+                    return B;
+                }
+
+                /*
+                 * Fast path for small A, with performance dependent only on the number of set bits
+                 */
+                long[] c0 = new long[cLen];
+                MultiplyWord(a0, B.m_ints, bLen, c0, 0);
+
+                /*
+                 * Reduce the raw answer against the reduction coefficients
+                 */
+                return ReduceResult(c0, 0, cLen, m, ks);
+            }
+
+            // NOTE: This works, but is slower than width 4 processing
+    //        if (aLen == 2)
+    //        {
+    //            /*
+    //             * Use common-multiplicand optimization to save ~1/4 of the adds
+    //             */
+    //            long a1 = A.m_ints[0], a2 = A.m_ints[1];
+    //            long aa = a1 & a2; a1 ^= aa; a2 ^= aa;
+    //
+    //            long[] b = B.m_ints;
+    //            long[] c = new long[cLen];
+    //            multiplyWord(aa, b, bLen, c, 1);
+    //            add(c, 0, c, 1, cLen - 1);
+    //            multiplyWord(a1, b, bLen, c, 0);
+    //            multiplyWord(a2, b, bLen, c, 1);
+    //
+    //            /*
+    //             * Reduce the raw answer against the reduction coefficients
+    //             */
+    //            return ReduceResult(c, 0, cLen, m, ks);
+    //        }
+
+            /*
+             * Determine the parameters of the Interleaved window algorithm: the 'width' in bits to
+             * process together, the number of evaluation 'positions' implied by that width, and the
+             * 'top' position at which the regular window algorithm stops.
+             */
+            int width, positions, top, banks;
+
+            // NOTE: width 4 is the fastest over the entire range of sizes used in current crypto 
+    //        width = 1; positions = 64; top = 64; banks = 4;
+    //        width = 2; positions = 32; top = 64; banks = 4;
+    //        width = 3; positions = 21; top = 63; banks = 3;
+            width = 4; positions = 16; top = 64; banks = 8;
+    //        width = 5; positions = 13; top = 65; banks = 7;
+    //        width = 7; positions = 9; top = 63; banks = 9;
+    //        width = 8; positions = 8; top = 64; banks = 8;
+
+            /*
+             * Determine if B will get bigger during shifting
+             */
+            int shifts = top < 64 ? positions : positions - 1;
+            int bMax = (int)((uint)(bDeg + shifts + 63) >> 6);
+
+            int bTotal = bMax * banks, stride = width * banks;
+
+            /*
+             * Create a single temporary buffer, with an offset table to find the positions of things in it 
+             */
+            int[] ci = new int[1 << width];
+            int cTotal = aLen;
+            {
+                ci[0] = cTotal;
+                cTotal += bTotal;
+                ci[1] = cTotal;
+                for (int i = 2; i < ci.Length; ++i)
+                {
+                    cTotal += cLen;
+                    ci[i] = cTotal;
+                }
+                cTotal += cLen;
+            }
+            // NOTE: Provide a safe dump for "high zeroes" since we are adding 'bMax' and not 'bLen'
+            ++cTotal;
+
+            long[] c = new long[cTotal];
+
+            // Prepare A in Interleaved form, according to the chosen width
+            Interleave(A.m_ints, 0, c, 0, aLen, width);
+
+            // Make a working copy of B, since we will be shifting it
+            {
+                int bOff = aLen;
+                Array.Copy(B.m_ints, 0, c, bOff, bLen);
+                for (int bank = 1; bank < banks; ++bank)
+                {
+                    ShiftUp(c, aLen, c, bOff += bMax, bMax, bank);
+                }
+            }
+
+            /*
+             * The main loop analyzes the Interleaved windows in A, and for each non-zero window
+             * a single word-array XOR is performed to a carefully selected slice of 'c'. The loop is
+             * breadth-first, checking the lowest window in each word, then looping again for the
+             * next higher window position.
+             */
+            int MASK = (1 << width) - 1;
+
+            int k = 0;
+            for (;;)
+            {
+                int aPos = 0;
+                do
+                {
+                    long aVal = (long)((ulong)c[aPos] >> k);
+                    int bank = 0, bOff = aLen;
+                    for (;;)
+                    {
+                        int index = (int)(aVal) & MASK;
+                        if (index != 0)
+                        {
+                            /*
+                             * Add to a 'c' buffer based on the bit-pattern of 'index'. Since A is in
+                             * Interleaved form, the bits represent the current B shifted by 0, 'positions',
+                             * 'positions' * 2, ..., 'positions' * ('width' - 1)
+                             */
+                            Add(c, aPos + ci[index], c, bOff, bMax);
+                        }
+                        if (++bank == banks)
+                        {
+                            break;
+                        }
+                        bOff += bMax;
+                        aVal = (long)((ulong)aVal >> width);
+                    }
+                }
+                while (++aPos < aLen);
+
+                if ((k += stride) >= top)
+                {
+                    if (k >= 64)
+                    {
+                        break;
+                    }
+
+                    /*
+                     * Adjustment for window setups with top == 63, the final bit (if any) is processed
+                     * as the top-bit of a window
+                     */
+                    k = 64 - width;
+                    MASK &= MASK << (top - k);
+                }
+
+                /*
+                 * After each position has been checked for all words of A, B is shifted up 1 place
+                 */
+                ShiftUp(c, aLen, bTotal, banks);
+            }
+
+            int ciPos = ci.Length;
+            while (--ciPos > 1)
+            {
+                if ((ciPos & 1L) == 0L)
+                {
+                    /*
+                     * For even numbers, shift contents and add to the half-position
+                     */
+                    AddShiftedUp(c, ci[(uint)ciPos >> 1], c, ci[ciPos], cLen, positions);
+                }
+                else
+                {
+                    /*
+                     * For odd numbers, 'distribute' contents to the result and the next-lowest position
+                     */
+                    Distribute(c, ci[ciPos], ci[ciPos - 1], ci[1], cLen);
+                }
+            }
+
+            /*
+             * Finally the raw answer is collected, reduce it against the reduction coefficients
+             */
+            return ReduceResult(c, ci[1], cLen, m, ks);
+        }
+
+        public LongArray ModReduce(int m, int[] ks)
+        {
+            long[] buf = Arrays.Clone(m_ints);
+            int rLen = ReduceInPlace(buf, 0, buf.Length, m, ks);
+            return new LongArray(buf, 0, rLen);
+        }
+
+        public LongArray Multiply(LongArray other, int m, int[] ks)
+        {
+            /*
+             * Find out the degree of each argument and handle the zero cases
+             */
+            int aDeg = Degree();
+            if (aDeg == 0)
+            {
+                return this;
+            }
+            int bDeg = other.Degree();
+            if (bDeg == 0)
+            {
+                return other;
+            }
+
+            /*
+             * Swap if necessary so that A is the smaller argument
+             */
+            LongArray A = this, B = other;
+            if (aDeg > bDeg)
+            {
+                A = other; B = this;
+                int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+            }
+
+            /*
+             * Establish the word lengths of the arguments and result
+             */
+            int aLen = (int)((uint)(aDeg + 63) >> 6);
+            int bLen = (int)((uint)(bDeg + 63) >> 6);
+            int cLen = (int)((uint)(aDeg + bDeg + 62) >> 6);
+
+            if (aLen == 1)
+            {
+                long a0 = A.m_ints[0];
+                if (a0 == 1L)
+                {
+                    return B;
+                }
+
+                /*
+                 * Fast path for small A, with performance dependent only on the number of set bits
+                 */
+                long[] c0 = new long[cLen];
+                MultiplyWord(a0, B.m_ints, bLen, c0, 0);
+
+                /*
+                 * Reduce the raw answer against the reduction coefficients
+                 */
+                //return ReduceResult(c0, 0, cLen, m, ks);
+                return new LongArray(c0, 0, cLen);
+            }
+
+            /*
+             * Determine if B will get bigger during shifting
+             */
+            int bMax = (int)((uint)(bDeg + 7 + 63) >> 6);
+
+            /*
+             * Lookup table for the offset of each B in the tables
+             */
+            int[] ti = new int[16];
+
+            /*
+             * Precompute table of all 4-bit products of B
+             */
+            long[] T0 = new long[bMax << 4];
+            int tOff = bMax;
+            ti[1] = tOff;
+            Array.Copy(B.m_ints, 0, T0, tOff, bLen);
+            for (int i = 2; i < 16; ++i)
+            {
+                ti[i] = (tOff += bMax);
+                if ((i & 1) == 0)
+                {
+                    ShiftUp(T0, (int)((uint)tOff >> 1), T0, tOff, bMax, 1);
+                }
+                else
+                {
+                    Add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax);
+                }
+            }
+
+            /*
+             * Second table with all 4-bit products of B shifted 4 bits
+             */
+            long[] T1 = new long[T0.Length];
+            ShiftUp(T0, 0, T1, 0, T0.Length, 4);
+            //        ShiftUp(T0, bMax, T1, bMax, tOff, 4);
+
+            long[] a = A.m_ints;
+            long[] c = new long[cLen << 3];
+
+            int MASK = 0xF;
+
+            /*
+             * Lopez-Dahab (Modified) algorithm
+             */
+
+            for (int aPos = 0; aPos < aLen; ++aPos)
+            {
+                long aVal = a[aPos];
+                int cOff = aPos;
+                for (; ; )
+                {
+                    int u = (int)aVal & MASK;
+                    aVal = (long)((ulong)aVal >> 4);
+                    int v = (int)aVal & MASK;
+                    AddBoth(c, cOff, T0, ti[u], T1, ti[v], bMax);
+                    aVal = (long)((ulong)aVal >> 4);
+                    if (aVal == 0L)
+                    {
+                        break;
+                    }
+                    cOff += cLen;
+                }
+            }
+
+            {
+                int cOff = c.Length;
+                while ((cOff -= cLen) != 0)
+                {
+                    AddShiftedUp(c, cOff - cLen, c, cOff, cLen, 8);
+                }
+            }
+
+            /*
+             * Finally the raw answer is collected, reduce it against the reduction coefficients
+             */
+            //return ReduceResult(c, 0, cLen, m, ks);
+            return new LongArray(c, 0, cLen);
+        }
+
+        public void Reduce(int m, int[] ks)
+        {
+            long[] buf = m_ints;
+            int rLen = ReduceInPlace(buf, 0, buf.Length, m, ks);
+            if (rLen < buf.Length)
+            {
+                m_ints = new long[rLen];
+                Array.Copy(buf, 0, m_ints, 0, rLen);
+            }
+        }
+
+        private static LongArray ReduceResult(long[] buf, int off, int len, int m, int[] ks)
+        {
+            int rLen = ReduceInPlace(buf, off, len, m, ks);
+            return new LongArray(buf, off, rLen);
+        }
+
+    //    private static void deInterleave(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+    //    {
+    //        for (int i = 0; i < count; ++i)
+    //        {
+    //            z[zOff + i] = deInterleave(x[zOff + i], rounds);
+    //        }
+    //    }
+    //
+    //    private static long deInterleave(long x, int rounds)
+    //    {
+    //        while (--rounds >= 0)
+    //        {
+    //            x = deInterleave32(x & DEInterleave_MASK) | (deInterleave32((x >>> 1) & DEInterleave_MASK) << 32);
+    //        }
+    //        return x;
+    //    }
+    //
+    //    private static long deInterleave32(long x)
+    //    {
+    //        x = (x | (x >>> 1)) & 0x3333333333333333L;
+    //        x = (x | (x >>> 2)) & 0x0F0F0F0F0F0F0F0FL;
+    //        x = (x | (x >>> 4)) & 0x00FF00FF00FF00FFL;
+    //        x = (x | (x >>> 8)) & 0x0000FFFF0000FFFFL;
+    //        x = (x | (x >>> 16)) & 0x00000000FFFFFFFFL;
+    //        return x;
+    //    }
+
+        private static int ReduceInPlace(long[] buf, int off, int len, int m, int[] ks)
+        {
+            int mLen = (m + 63) >> 6;
+            if (len < mLen)
+            {
+                return len;
+            }
+
+            int numBits = System.Math.Min(len << 6, (m << 1) - 1); // TODO use actual degree?
+            int excessBits = (len << 6) - numBits;
+            while (excessBits >= 64)
+            {
+                --len;
+                excessBits -= 64;
+            }
+
+            int kLen = ks.Length, kMax = ks[kLen - 1], kNext = kLen > 1 ? ks[kLen - 2] : 0;
+            int wordWiseLimit = System.Math.Max(m, kMax + 64);
+            int vectorableWords = (excessBits + System.Math.Min(numBits - wordWiseLimit, m - kNext)) >> 6;
+            if (vectorableWords > 1)
+            {
+                int vectorWiseWords = len - vectorableWords;
+                ReduceVectorWise(buf, off, len, vectorWiseWords, m, ks);
+                while (len > vectorWiseWords)
+                {
+                    buf[off + --len] = 0L;
+                }
+                numBits = vectorWiseWords << 6;
+            }
+
+            if (numBits > wordWiseLimit)
+            {
+                ReduceWordWise(buf, off, len, wordWiseLimit, m, ks);
+                numBits = wordWiseLimit;
+            }
+
+            if (numBits > m)
+            {
+                ReduceBitWise(buf, off, numBits, m, ks);
+            }
+
+            return mLen;
+        }
+
+        private static void ReduceBitWise(long[] buf, int off, int BitLength, int m, int[] ks)
+        {
+            while (--BitLength >= m)
+            {
+                if (TestBit(buf, off, BitLength))
+                {
+                    ReduceBit(buf, off, BitLength, m, ks);
+                }
+            }
+        }
+
+        private static void ReduceBit(long[] buf, int off, int bit, int m, int[] ks)
+        {
+            FlipBit(buf, off, bit);
+            int n = bit - m;
+            int j = ks.Length;
+            while (--j >= 0)
+            {
+                FlipBit(buf, off, ks[j] + n);
+            }
+            FlipBit(buf, off, n);
+        }
+
+        private static void ReduceWordWise(long[] buf, int off, int len, int toBit, int m, int[] ks)
+        {
+            int toPos = (int)((uint)toBit >> 6);
+
+            while (--len > toPos)
+            {
+                long word = buf[off + len];
+                if (word != 0)
+                {
+                    buf[off + len] = 0;
+                    ReduceWord(buf, off, (len << 6), word, m, ks);
+                }
+            }
+
+            {
+                int partial = toBit & 0x3F;
+                long word = (long)((ulong)buf[off + toPos] >> partial);
+                if (word != 0)
+                {
+                    buf[off + toPos] ^= word << partial;
+                    ReduceWord(buf, off, toBit, word, m, ks);
+                }
+            }
+        }
+
+        private static void ReduceWord(long[] buf, int off, int bit, long word, int m, int[] ks)
+        {
+            int offset = bit - m;
+            int j = ks.Length;
+            while (--j >= 0)
+            {
+                FlipWord(buf, off, offset + ks[j], word);
+            }
+            FlipWord(buf, off, offset, word);
+        }
+
+        private static void ReduceVectorWise(long[] buf, int off, int len, int words, int m, int[] ks)
+        {
+            /*
+             * NOTE: It's important we go from highest coefficient to lowest, because for the highest
+             * one (only) we allow the ranges to partially overlap, and therefore any changes must take
+             * effect for the subsequent lower coefficients.
+             */
+            int baseBit = (words << 6) - m;
+            int j = ks.Length;
+            while (--j >= 0)
+            {
+                FlipVector(buf, off, buf, off + words, len - words, baseBit + ks[j]);
+            }
+            FlipVector(buf, off, buf, off + words, len - words, baseBit);
+        }
+
+        private static void FlipVector(long[] x, int xOff, long[] y, int yOff, int yLen, int bits)
+        {
+            xOff += (int)((uint)bits >> 6);
+            bits &= 0x3F;
+
+            if (bits == 0)
+            {
+                Add(x, xOff, y, yOff, yLen);
+            }
+            else
+            {
+                long carry = AddShiftedDown(x, xOff + 1, y, yOff, yLen, 64 - bits);
+                x[xOff] ^= carry;
+            }
+        }
+
+        public LongArray ModSquare(int m, int[] ks)
+        {
+            int len = GetUsedLength();
+            if (len == 0)
+            {
+                return this;
+            }
+
+            int _2len = len << 1;
+            long[] r = new long[_2len];
+
+            int pos = 0;
+            while (pos < _2len)
+            {
+                long mi = m_ints[(uint)pos >> 1];
+                r[pos++] = Interleave2_32to64((int)mi);
+                r[pos++] = Interleave2_32to64((int)((ulong)mi >> 32));
+            }
+
+            return new LongArray(r, 0, ReduceInPlace(r, 0, r.Length, m, ks));
+        }
+
+        public LongArray ModSquareN(int n, int m, int[] ks)
+        {
+            int len = GetUsedLength();
+            if (len == 0)
+            {
+                return this;
+            }
+    
+            int mLen = (m + 63) >> 6;
+            long[] r = new long[mLen << 1];
+            Array.Copy(m_ints, 0, r, 0, len);
+    
+            while (--n >= 0)
+            {
+                SquareInPlace(r, len, m, ks);
+                len = ReduceInPlace(r, 0, r.Length, m, ks);
+            }
+    
+            return new LongArray(r, 0, len);
+        }
+
+        public LongArray Square(int m, int[] ks)
+        {
+            int len = GetUsedLength();
+            if (len == 0)
+            {
+                return this;
+            }
+
+            int _2len = len << 1;
+            long[] r = new long[_2len];
+
+            int pos = 0;
+            while (pos < _2len)
+            {
+                long mi = m_ints[(uint)pos >> 1];
+                r[pos++] = Interleave2_32to64((int)mi);
+                r[pos++] = Interleave2_32to64((int)((ulong)mi >> 32));
+            }
+
+            return new LongArray(r, 0, r.Length);
+        }
+
+        private static void SquareInPlace(long[] x, int xLen, int m, int[] ks)
+        {
+            int pos = xLen << 1;
+            while (--xLen >= 0)
+            {
+                long xVal = x[xLen];
+                x[--pos] = Interleave2_32to64((int)((ulong)xVal >> 32));
+                x[--pos] = Interleave2_32to64((int)xVal);
+            }
+        }
+
+        private static void Interleave(long[] x, int xOff, long[] z, int zOff, int count, int width)
+        {
+            switch (width)
+            {
+            case 3:
+                Interleave3(x, xOff, z, zOff, count);
+                break;
+            case 5:
+                Interleave5(x, xOff, z, zOff, count);
+                break;
+            case 7:
+                Interleave7(x, xOff, z, zOff, count);
+                break;
+            default:
+                Interleave2_n(x, xOff, z, zOff, count, BitLengths[width] - 1);
+                break;
+            }
+        }
+
+        private static void Interleave3(long[] x, int xOff, long[] z, int zOff, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                z[zOff + i] = Interleave3(x[xOff + i]);
+            }
+        }
+
+        private static long Interleave3(long x)
+        {
+            long z = x & (1L << 63);
+            return z
+                | Interleave3_21to63((int)x & 0x1FFFFF)
+                | Interleave3_21to63((int)((ulong)x >> 21) & 0x1FFFFF) << 1
+                | Interleave3_21to63((int)((ulong)x >> 42) & 0x1FFFFF) << 2;
+
+    //        int zPos = 0, wPos = 0, xPos = 0;
+    //        for (;;)
+    //        {
+    //            z |= ((x >>> xPos) & 1L) << zPos;
+    //            if (++zPos == 63)
+    //            {
+    //                String sz2 = Long.toBinaryString(z);
+    //                return z;
+    //            }
+    //            if ((xPos += 21) >= 63)
+    //            {
+    //                xPos = ++wPos;
+    //            }
+    //        }
+        }
+
+        private static long Interleave3_21to63(int x)
+        {
+            int r00 = INTERLEAVE3_TABLE[x & 0x7F];
+            int r21 = INTERLEAVE3_TABLE[((uint)x >> 7) & 0x7F];
+            int r42 = INTERLEAVE3_TABLE[(uint)x >> 14];
+            return (r42 & 0xFFFFFFFFL) << 42 | (r21 & 0xFFFFFFFFL) << 21 | (r00 & 0xFFFFFFFFL);
+        }
+
+        private static void Interleave5(long[] x, int xOff, long[] z, int zOff, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                z[zOff + i] = Interleave5(x[xOff + i]);
+            }
+        }
+
+        private static long Interleave5(long x)
+        {
+            return Interleave3_13to65((int)x & 0x1FFF)
+                | Interleave3_13to65((int)((ulong)x >> 13) & 0x1FFF) << 1
+                | Interleave3_13to65((int)((ulong)x >> 26) & 0x1FFF) << 2
+                | Interleave3_13to65((int)((ulong)x >> 39) & 0x1FFF) << 3
+                | Interleave3_13to65((int)((ulong)x >> 52) & 0x1FFF) << 4;
+
+    //        long z = 0;
+    //        int zPos = 0, wPos = 0, xPos = 0;
+    //        for (;;)
+    //        {
+    //            z |= ((x >>> xPos) & 1L) << zPos;
+    //            if (++zPos == 64)
+    //            {
+    //                return z;
+    //            }
+    //            if ((xPos += 13) >= 64)
+    //            {
+    //                xPos = ++wPos;
+    //            }
+    //        }
+        }
+
+        private static long Interleave3_13to65(int x)
+        {
+            int r00 = INTERLEAVE5_TABLE[x & 0x7F];
+            int r35 = INTERLEAVE5_TABLE[(uint)x >> 7];
+            return (r35 & 0xFFFFFFFFL) << 35 | (r00 & 0xFFFFFFFFL);
+        }
+
+        private static void Interleave7(long[] x, int xOff, long[] z, int zOff, int count)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                z[zOff + i] = Interleave7(x[xOff + i]);
+            }
+        }
+
+        private static long Interleave7(long x)
+        {
+            long z = x & (1L << 63);
+            return z
+                | INTERLEAVE7_TABLE[(int)x & 0x1FF]
+                | INTERLEAVE7_TABLE[(int)((ulong)x >> 9) & 0x1FF] << 1
+                | INTERLEAVE7_TABLE[(int)((ulong)x >> 18) & 0x1FF] << 2
+                | INTERLEAVE7_TABLE[(int)((ulong)x >> 27) & 0x1FF] << 3
+                | INTERLEAVE7_TABLE[(int)((ulong)x >> 36) & 0x1FF] << 4
+                | INTERLEAVE7_TABLE[(int)((ulong)x >> 45) & 0x1FF] << 5
+                | INTERLEAVE7_TABLE[(int)((ulong)x >> 54) & 0x1FF] << 6;
+
+    //        int zPos = 0, wPos = 0, xPos = 0;
+    //        for (;;)
+    //        {
+    //            z |= ((x >>> xPos) & 1L) << zPos;
+    //            if (++zPos == 63)
+    //            {
+    //                return z;
+    //            }
+    //            if ((xPos += 9) >= 63)
+    //            {
+    //                xPos = ++wPos;
+    //            }
+    //        }
+        }
+
+        private static void Interleave2_n(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+        {
+            for (int i = 0; i < count; ++i)
+            {
+                z[zOff + i] = Interleave2_n(x[xOff + i], rounds);
+            }
+        }
+
+        private static long Interleave2_n(long x, int rounds)
+        {
+            while (rounds > 1)
+            {
+                rounds -= 2;
+                x = Interleave4_16to64((int)x & 0xFFFF)
+                    | Interleave4_16to64((int)((ulong)x >> 16) & 0xFFFF) << 1
+                    | Interleave4_16to64((int)((ulong)x >> 32) & 0xFFFF) << 2
+                    | Interleave4_16to64((int)((ulong)x >> 48) & 0xFFFF) << 3;
+            }
+            if (rounds > 0)
+            {
+                x = Interleave2_32to64((int)x) | Interleave2_32to64((int)((ulong)x >> 32)) << 1;
+            }
+            return x;
+        }
+
+        private static long Interleave4_16to64(int x)
+        {
+            int r00 = INTERLEAVE4_TABLE[x & 0xFF];
+            int r32 = INTERLEAVE4_TABLE[(uint)x >> 8];
+            return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+        }
+
+        private static long Interleave2_32to64(int x)
+        {
+            int r00 = INTERLEAVE2_TABLE[x & 0xFF] | INTERLEAVE2_TABLE[((uint)x >> 8) & 0xFF] << 16;
+            int r32 = INTERLEAVE2_TABLE[((uint)x >> 16) & 0xFF] | INTERLEAVE2_TABLE[(uint)x >> 24] << 16;
+            return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+        }
+
+    //    private static LongArray ExpItohTsujii2(LongArray B, int n, int m, int[] ks)
+    //    {
+    //        LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+    //        int scale = 1;
+    //
+    //        int numTerms = n;
+    //        while (numTerms > 1)
+    //        {
+    //            if ((numTerms & 1) != 0)
+    //            {
+    //                t3 = t3.ModMultiply(t1, m, ks);
+    //                t1 = t1.modSquareN(scale, m, ks);
+    //            }
+    //
+    //            LongArray t2 = t1.modSquareN(scale, m, ks);
+    //            t1 = t1.ModMultiply(t2, m, ks);
+    //            numTerms >>>= 1; scale <<= 1;
+    //        }
+    //
+    //        return t3.ModMultiply(t1, m, ks);
+    //    }
+    //
+    //    private static LongArray ExpItohTsujii23(LongArray B, int n, int m, int[] ks)
+    //    {
+    //        LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+    //        int scale = 1;
+    //
+    //        int numTerms = n;
+    //        while (numTerms > 1)
+    //        {
+    //            bool m03 = numTerms % 3 == 0;
+    //            bool m14 = !m03 && (numTerms & 1) != 0;
+    //
+    //            if (m14)
+    //            {
+    //                t3 = t3.ModMultiply(t1, m, ks);
+    //                t1 = t1.modSquareN(scale, m, ks);
+    //            }
+    //
+    //            LongArray t2 = t1.modSquareN(scale, m, ks);
+    //            t1 = t1.ModMultiply(t2, m, ks);
+    //
+    //            if (m03)
+    //            {
+    //                t2 = t2.modSquareN(scale, m, ks);
+    //                t1 = t1.ModMultiply(t2, m, ks);
+    //                numTerms /= 3; scale *= 3;
+    //            }
+    //            else
+    //            {
+    //                numTerms >>>= 1; scale <<= 1;
+    //            }
+    //        }
+    //
+    //        return t3.ModMultiply(t1, m, ks);
+    //    }
+    //
+    //    private static LongArray ExpItohTsujii235(LongArray B, int n, int m, int[] ks)
+    //    {
+    //        LongArray t1 = B, t4 = new LongArray(new long[]{ 1L });
+    //        int scale = 1;
+    //
+    //        int numTerms = n;
+    //        while (numTerms > 1)
+    //        {
+    //            if (numTerms % 5 == 0)
+    //            {
+    ////                t1 = ExpItohTsujii23(t1, 5, m, ks);
+    //
+    //                LongArray t3 = t1;
+    //                t1 = t1.modSquareN(scale, m, ks);
+    //
+    //                LongArray t2 = t1.modSquareN(scale, m, ks);
+    //                t1 = t1.ModMultiply(t2, m, ks);
+    //                t2 = t1.modSquareN(scale << 1, m, ks);
+    //                t1 = t1.ModMultiply(t2, m, ks);
+    //
+    //                t1 = t1.ModMultiply(t3, m, ks);
+    //
+    //                numTerms /= 5; scale *= 5;
+    //                continue;
+    //            }
+    //
+    //            bool m03 = numTerms % 3 == 0;
+    //            bool m14 = !m03 && (numTerms & 1) != 0;
+    //
+    //            if (m14)
+    //            {
+    //                t4 = t4.ModMultiply(t1, m, ks);
+    //                t1 = t1.modSquareN(scale, m, ks);
+    //            }
+    //
+    //            LongArray t2 = t1.modSquareN(scale, m, ks);
+    //            t1 = t1.ModMultiply(t2, m, ks);
+    //
+    //            if (m03)
+    //            {
+    //                t2 = t2.modSquareN(scale, m, ks);
+    //                t1 = t1.ModMultiply(t2, m, ks);
+    //                numTerms /= 3; scale *= 3;
+    //            }
+    //            else
+    //            {
+    //                numTerms >>>= 1; scale <<= 1;
+    //            }
+    //        }
+    //
+    //        return t4.ModMultiply(t1, m, ks);
+    //    }
+
+        public LongArray ModInverse(int m, int[] ks)
+        {
+            /*
+             * Fermat's Little Theorem
+             */
+    //        LongArray A = this;
+    //        LongArray B = A.modSquare(m, ks);
+    //        LongArray R0 = B, R1 = B;
+    //        for (int i = 2; i < m; ++i)
+    //        {
+    //            R1 = R1.modSquare(m, ks);
+    //            R0 = R0.ModMultiply(R1, m, ks);
+    //        }
+    //
+    //        return R0;
+
+            /*
+             * Itoh-Tsujii
+             */
+    //        LongArray B = modSquare(m, ks);
+    //        switch (m)
+    //        {
+    //        case 409:
+    //            return ExpItohTsujii23(B, m - 1, m, ks);
+    //        case 571:
+    //            return ExpItohTsujii235(B, m - 1, m, ks);
+    //        case 163:
+    //        case 233:
+    //        case 283:
+    //        default:
+    //            return ExpItohTsujii2(B, m - 1, m, ks);
+    //        }
+
+            /*
+             * Inversion in F2m using the extended Euclidean algorithm
+             * 
+             * Input: A nonzero polynomial a(z) of degree at most m-1
+             * Output: a(z)^(-1) mod f(z)
+             */
+            int uzDegree = Degree();
+            if (uzDegree == 0)
+            {
+                throw new InvalidOperationException();
+            }
+            if (uzDegree == 1)
+            {
+                return this;
+            }
+
+            // u(z) := a(z)
+            LongArray uz = (LongArray)Copy();
+
+            int t = (m + 63) >> 6;
+
+            // v(z) := f(z)
+            LongArray vz = new LongArray(t);
+            ReduceBit(vz.m_ints, 0, m, m, ks);
+
+            // g1(z) := 1, g2(z) := 0
+            LongArray g1z = new LongArray(t);
+            g1z.m_ints[0] = 1L;
+            LongArray g2z = new LongArray(t);
+
+            int[] uvDeg = new int[]{ uzDegree, m + 1 };
+            LongArray[] uv = new LongArray[]{ uz, vz };
+
+            int[] ggDeg = new int[]{ 1, 0 };
+            LongArray[] gg = new LongArray[]{ g1z, g2z };
+
+            int b = 1;
+            int duv1 = uvDeg[b];
+            int dgg1 = ggDeg[b];
+            int j = duv1 - uvDeg[1 - b];
+
+            for (;;)
+            {
+                if (j < 0)
+                {
+                    j = -j;
+                    uvDeg[b] = duv1;
+                    ggDeg[b] = dgg1;
+                    b = 1 - b;
+                    duv1 = uvDeg[b];
+                    dgg1 = ggDeg[b];
+                }
+
+                uv[b].AddShiftedByBitsSafe(uv[1 - b], uvDeg[1 - b], j);
+
+                int duv2 = uv[b].DegreeFrom(duv1);
+                if (duv2 == 0)
+                {
+                    return gg[1 - b];
+                }
+
+                {
+                    int dgg2 = ggDeg[1 - b];
+                    gg[b].AddShiftedByBitsSafe(gg[1 - b], dgg2, j);
+                    dgg2 += j;
+
+                    if (dgg2 > dgg1)
+                    {
+                        dgg1 = dgg2;
+                    }
+                    else if (dgg2 == dgg1)
+                    {
+                        dgg1 = gg[b].DegreeFrom(dgg1);
+                    }
+                }
+
+                j += (duv2 - duv1);
+                duv1 = duv2;
+            }
+        }
+
+        public override bool Equals(object obj)
+        {
+            return Equals(obj as LongArray);
+        }
+
+        public virtual bool Equals(LongArray other)
+        {
+            if (this == other)
+                return true;
+            if (null == other)
+                return false;
+            int usedLen = GetUsedLength();
+            if (other.GetUsedLength() != usedLen)
+            {
+                return false;
+            }
+            for (int i = 0; i < usedLen; i++)
+            {
+                if (m_ints[i] != other.m_ints[i])
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            int usedLen = GetUsedLength();
+            int hash = 1;
+            for (int i = 0; i < usedLen; i++)
+            {
+                long mi = m_ints[i];
+                hash *= 31;
+                hash ^= (int)mi;
+                hash *= 31;
+                hash ^= (int)((ulong)mi >> 32);
+            }
+            return hash;
+        }
+
+        public LongArray Copy()
+        {
+            return new LongArray(Arrays.Clone(m_ints));
+        }
+
+        public override string ToString()
+        {
+            int i = GetUsedLength();
+            if (i == 0)
+            {
+                return "0";
+            }
+
+            StringBuilder sb = new StringBuilder(Convert.ToString(m_ints[--i], 2));
+            while (--i >= 0)
+            {
+                string s = Convert.ToString(m_ints[i], 2);
+
+                // Add leading zeroes, except for highest significant word
+                int len = s.Length;
+                if (len < 64)
+                {
+                    sb.Append(ZEROES.Substring(len));
+                }
+
+                sb.Append(s);
+            }
+            return sb.ToString();
+        }
+    }
+}

+ 241 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/SimpleBigDecimal.cs

@@ -0,0 +1,241 @@
+using System;
+using System.Text;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Abc
+{
+	/**
+	* Class representing a simple version of a big decimal. A
+	* <code>SimpleBigDecimal</code> is basically a
+	* {@link java.math.BigInteger BigInteger} with a few digits on the right of
+	* the decimal point. The number of (binary) digits on the right of the decimal
+	* point is called the <code>scale</code> of the <code>SimpleBigDecimal</code>.
+	* Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted
+	* automatically, but must be set manually. All <code>SimpleBigDecimal</code>s
+	* taking part in the same arithmetic operation must have equal scale. The
+	* result of a multiplication of two <code>SimpleBigDecimal</code>s returns a
+	* <code>SimpleBigDecimal</code> with double scale.
+	*/
+	internal class SimpleBigDecimal
+		//	: Number
+	{
+		//	private static final long serialVersionUID = 1L;
+
+		private readonly BigInteger	bigInt;
+		private readonly int		scale;
+
+		/**
+		* Returns a <code>SimpleBigDecimal</code> representing the same numerical
+		* value as <code>value</code>.
+		* @param value The value of the <code>SimpleBigDecimal</code> to be
+		* created. 
+		* @param scale The scale of the <code>SimpleBigDecimal</code> to be
+		* created. 
+		* @return The such created <code>SimpleBigDecimal</code>.
+		*/
+		public static SimpleBigDecimal GetInstance(BigInteger val, int scale)
+		{
+			return new SimpleBigDecimal(val.ShiftLeft(scale), scale);
+		}
+
+		/**
+		* Constructor for <code>SimpleBigDecimal</code>. The value of the
+		* constructed <code>SimpleBigDecimal</code> Equals <code>bigInt / 
+		* 2<sup>scale</sup></code>.
+		* @param bigInt The <code>bigInt</code> value parameter.
+		* @param scale The scale of the constructed <code>SimpleBigDecimal</code>.
+		*/
+		public SimpleBigDecimal(BigInteger bigInt, int scale)
+		{
+			if (scale < 0)
+				throw new ArgumentException("scale may not be negative");
+
+			this.bigInt = bigInt;
+			this.scale = scale;
+		}
+
+		private SimpleBigDecimal(SimpleBigDecimal limBigDec)
+		{
+			bigInt = limBigDec.bigInt;
+			scale = limBigDec.scale;
+		}
+
+		private void CheckScale(SimpleBigDecimal b)
+		{
+			if (scale != b.scale)
+				throw new ArgumentException("Only SimpleBigDecimal of same scale allowed in arithmetic operations");
+		}
+
+		public SimpleBigDecimal AdjustScale(int newScale)
+		{
+			if (newScale < 0)
+				throw new ArgumentException("scale may not be negative");
+
+			if (newScale == scale)
+				return this;
+
+			return new SimpleBigDecimal(bigInt.ShiftLeft(newScale - scale), newScale);
+		}
+
+		public SimpleBigDecimal Add(SimpleBigDecimal b)
+		{
+			CheckScale(b);
+			return new SimpleBigDecimal(bigInt.Add(b.bigInt), scale);
+		}
+
+		public SimpleBigDecimal Add(BigInteger b)
+		{
+			return new SimpleBigDecimal(bigInt.Add(b.ShiftLeft(scale)), scale);
+		}
+
+		public SimpleBigDecimal Negate()
+		{
+			return new SimpleBigDecimal(bigInt.Negate(), scale);
+		}
+
+		public SimpleBigDecimal Subtract(SimpleBigDecimal b)
+		{
+			return Add(b.Negate());
+		}
+
+		public SimpleBigDecimal Subtract(BigInteger b)
+		{
+			return new SimpleBigDecimal(bigInt.Subtract(b.ShiftLeft(scale)), scale);
+		}
+
+		public SimpleBigDecimal Multiply(SimpleBigDecimal b)
+		{
+			CheckScale(b);
+			return new SimpleBigDecimal(bigInt.Multiply(b.bigInt), scale + scale);
+		}
+
+		public SimpleBigDecimal Multiply(BigInteger b)
+		{
+			return new SimpleBigDecimal(bigInt.Multiply(b), scale);
+		}
+
+		public SimpleBigDecimal Divide(SimpleBigDecimal b)
+		{
+			CheckScale(b);
+			BigInteger dividend = bigInt.ShiftLeft(scale);
+			return new SimpleBigDecimal(dividend.Divide(b.bigInt), scale);
+		}
+
+		public SimpleBigDecimal Divide(BigInteger b)
+		{
+			return new SimpleBigDecimal(bigInt.Divide(b), scale);
+		}
+
+		public SimpleBigDecimal ShiftLeft(int n)
+		{
+			return new SimpleBigDecimal(bigInt.ShiftLeft(n), scale);
+		}
+
+		public int CompareTo(SimpleBigDecimal val)
+		{
+			CheckScale(val);
+			return bigInt.CompareTo(val.bigInt);
+		}
+
+		public int CompareTo(BigInteger val)
+		{
+			return bigInt.CompareTo(val.ShiftLeft(scale));
+		}
+
+		public BigInteger Floor()
+		{
+			return bigInt.ShiftRight(scale);
+		}
+
+		public BigInteger Round()
+		{
+			SimpleBigDecimal oneHalf = new SimpleBigDecimal(BigInteger.One, 1);
+			return Add(oneHalf.AdjustScale(scale)).Floor();
+		}
+
+		public int IntValue
+		{
+			get { return Floor().IntValue; }
+		}
+
+		public long LongValue
+		{
+			get { return Floor().LongValue; }
+		}
+
+//		public double doubleValue()
+//		{
+//			return new Double(ToString()).doubleValue();
+//		}
+//
+//		public float floatValue()
+//		{
+//			return new Float(ToString()).floatValue();
+//		}
+
+		public int Scale
+		{
+			get { return scale; }
+		}
+
+		public override string ToString()
+		{
+			if (scale == 0)
+				return bigInt.ToString();
+
+			BigInteger floorBigInt = Floor();
+	        
+			BigInteger fract = bigInt.Subtract(floorBigInt.ShiftLeft(scale));
+			if (bigInt.SignValue < 0)
+			{
+				fract = BigInteger.One.ShiftLeft(scale).Subtract(fract);
+			}
+
+			if ((floorBigInt.SignValue == -1) && (!(fract.Equals(BigInteger.Zero))))
+			{
+				floorBigInt = floorBigInt.Add(BigInteger.One);
+			}
+			string leftOfPoint = floorBigInt.ToString();
+
+			char[] fractCharArr = new char[scale];
+				string fractStr = fract.ToString(2);
+			int fractLen = fractStr.Length;
+			int zeroes = scale - fractLen;
+			for (int i = 0; i < zeroes; i++)
+			{
+				fractCharArr[i] = '0';
+			}
+			for (int j = 0; j < fractLen; j++)
+			{
+				fractCharArr[zeroes + j] = fractStr[j];
+			}
+			string rightOfPoint = new string(fractCharArr);
+
+			StringBuilder sb = new StringBuilder(leftOfPoint);
+			sb.Append(".");
+			sb.Append(rightOfPoint);
+
+			return sb.ToString();
+		}
+
+		public override bool Equals(
+			object obj)
+		{
+			if (this == obj)
+				return true;
+
+			SimpleBigDecimal other = obj as SimpleBigDecimal;
+
+			if (other == null)
+				return false;
+
+			return bigInt.Equals(other.bigInt)
+				&& scale == other.scale;
+		}
+
+		public override int GetHashCode()
+		{
+			return bigInt.GetHashCode() ^ scale;
+		}
+
+	}
+}

+ 845 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/Tnaf.cs

@@ -0,0 +1,845 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Abc
+{
+    /**
+    * Class holding methods for point multiplication based on the window
+    * &#964;-adic nonadjacent form (WTNAF). The algorithms are based on the
+    * paper "Improved Algorithms for Arithmetic on Anomalous Binary Curves"
+    * by Jerome A. Solinas. The paper first appeared in the Proceedings of
+    * Crypto 1997.
+    */
+    internal class Tnaf
+    {
+        private static readonly BigInteger MinusOne = BigInteger.One.Negate();
+        private static readonly BigInteger MinusTwo = BigInteger.Two.Negate();
+        private static readonly BigInteger MinusThree = BigInteger.Three.Negate();
+        private static readonly BigInteger Four = BigInteger.ValueOf(4);
+
+        /**
+        * The window width of WTNAF. The standard value of 4 is slightly less
+        * than optimal for running time, but keeps space requirements for
+        * precomputation low. For typical curves, a value of 5 or 6 results in
+        * a better running time. When changing this value, the
+        * <code>&#945;<sub>u</sub></code>'s must be computed differently, see
+        * e.g. "Guide to Elliptic Curve Cryptography", Darrel Hankerson,
+        * Alfred Menezes, Scott Vanstone, Springer-Verlag New York Inc., 2004,
+        * p. 121-122
+        */
+        public const sbyte Width = 4;
+
+        /**
+        * 2<sup>4</sup>
+        */
+        public const sbyte Pow2Width = 16;
+
+        /**
+        * The <code>&#945;<sub>u</sub></code>'s for <code>a=0</code> as an array
+        * of <code>ZTauElement</code>s.
+        */
+        public static readonly ZTauElement[] Alpha0 =
+        {
+            null,
+            new ZTauElement(BigInteger.One, BigInteger.Zero), null,
+            new ZTauElement(MinusThree, MinusOne), null,
+            new ZTauElement(MinusOne, MinusOne), null,
+            new ZTauElement(BigInteger.One, MinusOne), null
+        };
+
+        /**
+        * The <code>&#945;<sub>u</sub></code>'s for <code>a=0</code> as an array
+        * of TNAFs.
+        */
+        public static readonly sbyte[][] Alpha0Tnaf =
+        {
+            null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, 1}
+        };
+
+        /**
+        * The <code>&#945;<sub>u</sub></code>'s for <code>a=1</code> as an array
+        * of <code>ZTauElement</code>s.
+        */
+        public static readonly ZTauElement[] Alpha1 =
+        {
+            null,
+            new ZTauElement(BigInteger.One, BigInteger.Zero), null,
+            new ZTauElement(MinusThree, BigInteger.One), null,
+            new ZTauElement(MinusOne, BigInteger.One), null,
+            new ZTauElement(BigInteger.One, BigInteger.One), null
+        };
+
+        /**
+        * The <code>&#945;<sub>u</sub></code>'s for <code>a=1</code> as an array
+        * of TNAFs.
+        */
+        public static readonly sbyte[][] Alpha1Tnaf =
+        {
+            null, new sbyte[]{1}, null, new sbyte[]{-1, 0, 1}, null, new sbyte[]{1, 0, 1}, null, new sbyte[]{-1, 0, 0, -1}
+        };
+
+        /**
+        * Computes the norm of an element <code>&#955;</code> of
+        * <code><b>Z</b>[&#964;]</code>.
+        * @param mu The parameter <code>&#956;</code> of the elliptic curve.
+        * @param lambda The element <code>&#955;</code> of
+        * <code><b>Z</b>[&#964;]</code>.
+        * @return The norm of <code>&#955;</code>.
+        */
+        public static BigInteger Norm(sbyte mu, ZTauElement lambda)
+        {
+            BigInteger norm;
+
+            // s1 = u^2
+            BigInteger s1 = lambda.u.Multiply(lambda.u);
+
+            // s2 = u * v
+            BigInteger s2 = lambda.u.Multiply(lambda.v);
+
+            // s3 = 2 * v^2
+            BigInteger s3 = lambda.v.Multiply(lambda.v).ShiftLeft(1);
+
+            if (mu == 1)
+            {
+                norm = s1.Add(s2).Add(s3);
+            }
+            else if (mu == -1)
+            {
+                norm = s1.Subtract(s2).Add(s3);
+            }
+            else
+            {
+                throw new ArgumentException("mu must be 1 or -1");
+            }
+
+            return norm;
+        }
+
+        /**
+        * Computes the norm of an element <code>&#955;</code> of
+        * <code><b>R</b>[&#964;]</code>, where <code>&#955; = u + v&#964;</code>
+        * and <code>u</code> and <code>u</code> are real numbers (elements of
+        * <code><b>R</b></code>). 
+        * @param mu The parameter <code>&#956;</code> of the elliptic curve.
+        * @param u The real part of the element <code>&#955;</code> of
+        * <code><b>R</b>[&#964;]</code>.
+        * @param v The <code>&#964;</code>-adic part of the element
+        * <code>&#955;</code> of <code><b>R</b>[&#964;]</code>.
+        * @return The norm of <code>&#955;</code>.
+        */
+        public static SimpleBigDecimal Norm(sbyte mu, SimpleBigDecimal u, SimpleBigDecimal v)
+        {
+            SimpleBigDecimal norm;
+
+            // s1 = u^2
+            SimpleBigDecimal s1 = u.Multiply(u);
+
+            // s2 = u * v
+            SimpleBigDecimal s2 = u.Multiply(v);
+
+            // s3 = 2 * v^2
+            SimpleBigDecimal s3 = v.Multiply(v).ShiftLeft(1);
+
+            if (mu == 1)
+            {
+                norm = s1.Add(s2).Add(s3);
+            }
+            else if (mu == -1)
+            {
+                norm = s1.Subtract(s2).Add(s3);
+            }
+            else
+            {
+                throw new ArgumentException("mu must be 1 or -1");
+            }
+
+            return norm;
+        }
+
+        /**
+        * Rounds an element <code>&#955;</code> of <code><b>R</b>[&#964;]</code>
+        * to an element of <code><b>Z</b>[&#964;]</code>, such that their difference
+        * has minimal norm. <code>&#955;</code> is given as
+        * <code>&#955; = &#955;<sub>0</sub> + &#955;<sub>1</sub>&#964;</code>.
+        * @param lambda0 The component <code>&#955;<sub>0</sub></code>.
+        * @param lambda1 The component <code>&#955;<sub>1</sub></code>.
+        * @param mu The parameter <code>&#956;</code> of the elliptic curve. Must
+        * equal 1 or -1.
+        * @return The rounded element of <code><b>Z</b>[&#964;]</code>.
+        * @throws ArgumentException if <code>lambda0</code> and
+        * <code>lambda1</code> do not have same scale.
+        */
+        public static ZTauElement Round(SimpleBigDecimal lambda0,
+            SimpleBigDecimal lambda1, sbyte mu)
+        {
+            int scale = lambda0.Scale;
+            if (lambda1.Scale != scale)
+                throw new ArgumentException("lambda0 and lambda1 do not have same scale");
+
+            if (!((mu == 1) || (mu == -1)))
+                throw new ArgumentException("mu must be 1 or -1");
+
+            BigInteger f0 = lambda0.Round();
+            BigInteger f1 = lambda1.Round();
+
+            SimpleBigDecimal eta0 = lambda0.Subtract(f0);
+            SimpleBigDecimal eta1 = lambda1.Subtract(f1);
+
+            // eta = 2*eta0 + mu*eta1
+            SimpleBigDecimal eta = eta0.Add(eta0);
+            if (mu == 1)
+            {
+                eta = eta.Add(eta1);
+            }
+            else
+            {
+                // mu == -1
+                eta = eta.Subtract(eta1);
+            }
+
+            // check1 = eta0 - 3*mu*eta1
+            // check2 = eta0 + 4*mu*eta1
+            SimpleBigDecimal threeEta1 = eta1.Add(eta1).Add(eta1);
+            SimpleBigDecimal fourEta1 = threeEta1.Add(eta1);
+            SimpleBigDecimal check1;
+            SimpleBigDecimal check2;
+            if (mu == 1)
+            {
+                check1 = eta0.Subtract(threeEta1);
+                check2 = eta0.Add(fourEta1);
+            }
+            else
+            {
+                // mu == -1
+                check1 = eta0.Add(threeEta1);
+                check2 = eta0.Subtract(fourEta1);
+            }
+
+            sbyte h0 = 0;
+            sbyte h1 = 0;
+
+            // if eta >= 1
+            if (eta.CompareTo(BigInteger.One) >= 0)
+            {
+                if (check1.CompareTo(MinusOne) < 0)
+                {
+                    h1 = mu;
+                }
+                else
+                {
+                    h0 = 1;
+                }
+            }
+            else
+            {
+                // eta < 1
+                if (check2.CompareTo(BigInteger.Two) >= 0)
+                {
+                    h1 = mu;
+                }
+            }
+
+            // if eta < -1
+            if (eta.CompareTo(MinusOne) < 0)
+            {
+                if (check1.CompareTo(BigInteger.One) >= 0)
+                {
+                    h1 = (sbyte)-mu;
+                }
+                else
+                {
+                    h0 = -1;
+                }
+            }
+            else
+            {
+                // eta >= -1
+                if (check2.CompareTo(MinusTwo) < 0)
+                {
+                    h1 = (sbyte)-mu;
+                }
+            }
+
+            BigInteger q0 = f0.Add(BigInteger.ValueOf(h0));
+            BigInteger q1 = f1.Add(BigInteger.ValueOf(h1));
+            return new ZTauElement(q0, q1);
+        }
+
+        /**
+        * Approximate division by <code>n</code>. For an integer
+        * <code>k</code>, the value <code>&#955; = s k / n</code> is
+        * computed to <code>c</code> bits of accuracy.
+        * @param k The parameter <code>k</code>.
+        * @param s The curve parameter <code>s<sub>0</sub></code> or
+        * <code>s<sub>1</sub></code>.
+        * @param vm The Lucas Sequence element <code>V<sub>m</sub></code>.
+        * @param a The parameter <code>a</code> of the elliptic curve.
+        * @param m The bit length of the finite field
+        * <code><b>F</b><sub>m</sub></code>.
+        * @param c The number of bits of accuracy, i.e. the scale of the returned
+        * <code>SimpleBigDecimal</code>.
+        * @return The value <code>&#955; = s k / n</code> computed to
+        * <code>c</code> bits of accuracy.
+        */
+        public static SimpleBigDecimal ApproximateDivisionByN(BigInteger k,
+            BigInteger s, BigInteger vm, sbyte a, int m, int c)
+        {
+            int _k = (m + 5)/2 + c;
+            BigInteger ns = k.ShiftRight(m - _k - 2 + a);
+
+            BigInteger gs = s.Multiply(ns);
+
+            BigInteger hs = gs.ShiftRight(m);
+
+            BigInteger js = vm.Multiply(hs);
+
+            BigInteger gsPlusJs = gs.Add(js);
+            BigInteger ls = gsPlusJs.ShiftRight(_k-c);
+            if (gsPlusJs.TestBit(_k-c-1))
+            {
+                // round up
+                ls = ls.Add(BigInteger.One);
+            }
+
+            return new SimpleBigDecimal(ls, c);
+        }
+
+        /**
+        * Computes the <code>&#964;</code>-adic NAF (non-adjacent form) of an
+        * element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code>.
+        * @param mu The parameter <code>&#956;</code> of the elliptic curve.
+        * @param lambda The element <code>&#955;</code> of
+        * <code><b>Z</b>[&#964;]</code>.
+        * @return The <code>&#964;</code>-adic NAF of <code>&#955;</code>.
+        */
+        public static sbyte[] TauAdicNaf(sbyte mu, ZTauElement lambda)
+        {
+            if (!((mu == 1) || (mu == -1))) 
+                throw new ArgumentException("mu must be 1 or -1");
+
+            BigInteger norm = Norm(mu, lambda);
+
+            // Ceiling of log2 of the norm 
+            int log2Norm = norm.BitLength;
+
+            // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+            int maxLength = log2Norm > 30 ? log2Norm + 4 : 34;
+
+            // The array holding the TNAF
+            sbyte[] u = new sbyte[maxLength];
+            int i = 0;
+
+            // The actual length of the TNAF
+            int length = 0;
+
+            BigInteger r0 = lambda.u;
+            BigInteger r1 = lambda.v;
+
+            while(!((r0.Equals(BigInteger.Zero)) && (r1.Equals(BigInteger.Zero))))
+            {
+                // If r0 is odd
+                if (r0.TestBit(0)) 
+                {
+                    u[i] = (sbyte) BigInteger.Two.Subtract((r0.Subtract(r1.ShiftLeft(1))).Mod(Four)).IntValue;
+
+                    // r0 = r0 - u[i]
+                    if (u[i] == 1)
+                    {
+                        r0 = r0.ClearBit(0);
+                    }
+                    else
+                    {
+                        // u[i] == -1
+                        r0 = r0.Add(BigInteger.One);
+                    }
+                    length = i;
+                }
+                else
+                {
+                    u[i] = 0;
+                }
+
+                BigInteger t = r0;
+                BigInteger s = r0.ShiftRight(1);
+                if (mu == 1) 
+                {
+                    r0 = r1.Add(s);
+                }
+                else
+                {
+                    // mu == -1
+                    r0 = r1.Subtract(s);
+                }
+
+                r1 = t.ShiftRight(1).Negate();
+                i++;
+            }
+
+            length++;
+
+            // Reduce the TNAF array to its actual length
+            sbyte[] tnaf = new sbyte[length];
+            Array.Copy(u, 0, tnaf, 0, length);
+            return tnaf;
+        }
+
+        /**
+        * Applies the operation <code>&#964;()</code> to an
+        * <code>AbstractF2mPoint</code>. 
+        * @param p The AbstractF2mPoint to which <code>&#964;()</code> is applied.
+        * @return <code>&#964;(p)</code>
+        */
+        public static AbstractF2mPoint Tau(AbstractF2mPoint p)
+        {
+            return p.Tau();
+        }
+
+        /**
+        * Returns the parameter <code>&#956;</code> of the elliptic curve.
+        * @param curve The elliptic curve from which to obtain <code>&#956;</code>.
+        * The curve must be a Koblitz curve, i.e. <code>a</code> Equals
+        * <code>0</code> or <code>1</code> and <code>b</code> Equals
+        * <code>1</code>. 
+        * @return <code>&#956;</code> of the elliptic curve.
+        * @throws ArgumentException if the given ECCurve is not a Koblitz
+        * curve.
+        */
+        public static sbyte GetMu(AbstractF2mCurve curve)
+        {
+            BigInteger a = curve.A.ToBigInteger();
+
+            sbyte mu;
+            if (a.SignValue == 0)
+            {
+                mu = -1;
+            }
+            else if (a.Equals(BigInteger.One))
+            {
+                mu = 1;
+            }
+            else
+            {
+                throw new ArgumentException("No Koblitz curve (ABC), TNAF multiplication not possible");
+            }
+            return mu;
+        }
+
+        public static sbyte GetMu(ECFieldElement curveA)
+        {
+            return (sbyte)(curveA.IsZero ? -1 : 1);
+        }
+
+        public static sbyte GetMu(int curveA)
+        {
+            return (sbyte)(curveA == 0 ? -1 : 1);
+        }
+
+        /**
+        * Calculates the Lucas Sequence elements <code>U<sub>k-1</sub></code> and
+        * <code>U<sub>k</sub></code> or <code>V<sub>k-1</sub></code> and
+        * <code>V<sub>k</sub></code>.
+        * @param mu The parameter <code>&#956;</code> of the elliptic curve.
+        * @param k The index of the second element of the Lucas Sequence to be
+        * returned.
+        * @param doV If set to true, computes <code>V<sub>k-1</sub></code> and
+        * <code>V<sub>k</sub></code>, otherwise <code>U<sub>k-1</sub></code> and
+        * <code>U<sub>k</sub></code>.
+        * @return An array with 2 elements, containing <code>U<sub>k-1</sub></code>
+        * and <code>U<sub>k</sub></code> or <code>V<sub>k-1</sub></code>
+        * and <code>V<sub>k</sub></code>.
+        */
+        public static BigInteger[] GetLucas(sbyte mu, int k, bool doV)
+        {
+            if (!(mu == 1 || mu == -1)) 
+                throw new ArgumentException("mu must be 1 or -1");
+
+            BigInteger u0;
+            BigInteger u1;
+            BigInteger u2;
+
+            if (doV)
+            {
+                u0 = BigInteger.Two;
+                u1 = BigInteger.ValueOf(mu);
+            }
+            else
+            {
+                u0 = BigInteger.Zero;
+                u1 = BigInteger.One;
+            }
+
+            for (int i = 1; i < k; i++)
+            {
+                // u2 = mu*u1 - 2*u0;
+                BigInteger s = null;
+                if (mu == 1)
+                {
+                    s = u1;
+                }
+                else
+                {
+                    // mu == -1
+                    s = u1.Negate();
+                }
+                
+                u2 = s.Subtract(u0.ShiftLeft(1));
+                u0 = u1;
+                u1 = u2;
+                //            System.out.println(i + ": " + u2);
+                //            System.out.println();
+            }
+
+            BigInteger[] retVal = {u0, u1};
+            return retVal;
+        }
+
+        /**
+        * Computes the auxiliary value <code>t<sub>w</sub></code>. If the width is
+        * 4, then for <code>mu = 1</code>, <code>t<sub>w</sub> = 6</code> and for
+        * <code>mu = -1</code>, <code>t<sub>w</sub> = 10</code> 
+        * @param mu The parameter <code>&#956;</code> of the elliptic curve.
+        * @param w The window width of the WTNAF.
+        * @return the auxiliary value <code>t<sub>w</sub></code>
+        */
+        public static BigInteger GetTw(sbyte mu, int w) 
+        {
+            if (w == 4)
+            {
+                if (mu == 1)
+                {
+                    return BigInteger.ValueOf(6);
+                }
+                else
+                {
+                    // mu == -1
+                    return BigInteger.ValueOf(10);
+                }
+            }
+            else
+            {
+                // For w <> 4, the values must be computed
+                BigInteger[] us = GetLucas(mu, w, false);
+                BigInteger twoToW = BigInteger.Zero.SetBit(w);
+                BigInteger u1invert = us[1].ModInverse(twoToW);
+                BigInteger tw;
+                tw = BigInteger.Two.Multiply(us[0]).Multiply(u1invert).Mod(twoToW);
+                //System.out.println("mu = " + mu);
+                //System.out.println("tw = " + tw);
+                return tw;
+            }
+        }
+
+        /**
+        * Computes the auxiliary values <code>s<sub>0</sub></code> and
+        * <code>s<sub>1</sub></code> used for partial modular reduction. 
+        * @param curve The elliptic curve for which to compute
+        * <code>s<sub>0</sub></code> and <code>s<sub>1</sub></code>.
+        * @throws ArgumentException if <code>curve</code> is not a
+        * Koblitz curve (Anomalous Binary Curve, ABC).
+        */
+        public static BigInteger[] GetSi(AbstractF2mCurve curve)
+        {
+            if (!curve.IsKoblitz)
+                throw new ArgumentException("si is defined for Koblitz curves only");
+
+            int m = curve.FieldSize;
+            int a = curve.A.ToBigInteger().IntValue;
+            sbyte mu = GetMu(a);
+            int shifts = GetShiftsForCofactor(curve.Cofactor);
+            int index = m + 3 - a;
+            BigInteger[] ui = GetLucas(mu, index, false);
+
+            if (mu == 1)
+            {
+                ui[0] = ui[0].Negate();
+                ui[1] = ui[1].Negate();
+            }
+
+            BigInteger dividend0 = BigInteger.One.Add(ui[1]).ShiftRight(shifts);
+            BigInteger dividend1 = BigInteger.One.Add(ui[0]).ShiftRight(shifts).Negate();
+
+            return new BigInteger[] { dividend0, dividend1 };
+        }
+
+        public static BigInteger[] GetSi(int fieldSize, int curveA, BigInteger cofactor)
+        {
+            sbyte mu = GetMu(curveA);
+            int shifts = GetShiftsForCofactor(cofactor);
+            int index = fieldSize + 3 - curveA;
+            BigInteger[] ui = GetLucas(mu, index, false);
+            if (mu == 1)
+            {
+                ui[0] = ui[0].Negate();
+                ui[1] = ui[1].Negate();
+            }
+
+            BigInteger dividend0 = BigInteger.One.Add(ui[1]).ShiftRight(shifts);
+            BigInteger dividend1 = BigInteger.One.Add(ui[0]).ShiftRight(shifts).Negate();
+
+            return new BigInteger[] { dividend0, dividend1 };
+        }
+
+        protected static int GetShiftsForCofactor(BigInteger h)
+        {
+            if (h != null && h.BitLength < 4)
+            {
+                int hi = h.IntValue;
+                if (hi == 2)
+                    return 1;
+                if (hi == 4)
+                    return 2;
+            }
+
+            throw new ArgumentException("h (Cofactor) must be 2 or 4");
+        }
+
+        /**
+        * Partial modular reduction modulo
+        * <code>(&#964;<sup>m</sup> - 1)/(&#964; - 1)</code>.
+        * @param k The integer to be reduced.
+        * @param m The bitlength of the underlying finite field.
+        * @param a The parameter <code>a</code> of the elliptic curve.
+        * @param s The auxiliary values <code>s<sub>0</sub></code> and
+        * <code>s<sub>1</sub></code>.
+        * @param mu The parameter &#956; of the elliptic curve.
+        * @param c The precision (number of bits of accuracy) of the partial
+        * modular reduction.
+        * @return <code>&#961; := k partmod (&#964;<sup>m</sup> - 1)/(&#964; - 1)</code>
+        */
+        public static ZTauElement PartModReduction(BigInteger k, int m, sbyte a,
+            BigInteger[] s, sbyte mu, sbyte c)
+        {
+            // d0 = s[0] + mu*s[1]; mu is either 1 or -1
+            BigInteger d0;
+            if (mu == 1)
+            {
+                d0 = s[0].Add(s[1]);
+            }
+            else
+            {
+                d0 = s[0].Subtract(s[1]);
+            }
+
+            BigInteger[] v = GetLucas(mu, m, true);
+            BigInteger vm = v[1];
+
+            SimpleBigDecimal lambda0 = ApproximateDivisionByN(
+                k, s[0], vm, a, m, c);
+            
+            SimpleBigDecimal lambda1 = ApproximateDivisionByN(
+                k, s[1], vm, a, m, c);
+
+            ZTauElement q = Round(lambda0, lambda1, mu);
+
+            // r0 = n - d0*q0 - 2*s1*q1
+            BigInteger r0 = k.Subtract(d0.Multiply(q.u)).Subtract(
+                BigInteger.ValueOf(2).Multiply(s[1]).Multiply(q.v));
+
+            // r1 = s1*q0 - s0*q1
+            BigInteger r1 = s[1].Multiply(q.u).Subtract(s[0].Multiply(q.v));
+            
+            return new ZTauElement(r0, r1);
+        }
+
+        /**
+        * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint}
+        * by a <code>BigInteger</code> using the reduced <code>&#964;</code>-adic
+        * NAF (RTNAF) method.
+        * @param p The AbstractF2mPoint to Multiply.
+        * @param k The <code>BigInteger</code> by which to Multiply <code>p</code>.
+        * @return <code>k * p</code>
+        */
+        public static AbstractF2mPoint MultiplyRTnaf(AbstractF2mPoint p, BigInteger k)
+        {
+            AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve;
+            int m = curve.FieldSize;
+            int a = curve.A.ToBigInteger().IntValue;
+            sbyte mu = GetMu(a);
+            BigInteger[] s = curve.GetSi();
+            ZTauElement rho = PartModReduction(k, m, (sbyte)a, s, mu, (sbyte)10);
+
+            return MultiplyTnaf(p, rho);
+        }
+
+        /**
+        * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint}
+        * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code>
+        * using the <code>&#964;</code>-adic NAF (TNAF) method.
+        * @param p The AbstractF2mPoint to Multiply.
+        * @param lambda The element <code>&#955;</code> of
+        * <code><b>Z</b>[&#964;]</code>.
+        * @return <code>&#955; * p</code>
+        */
+        public static AbstractF2mPoint MultiplyTnaf(AbstractF2mPoint p, ZTauElement lambda)
+        {
+            AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve;
+            sbyte mu = GetMu(curve.A);
+            sbyte[] u = TauAdicNaf(mu, lambda);
+
+            AbstractF2mPoint q = MultiplyFromTnaf(p, u);
+
+            return q;
+        }
+
+        /**
+        * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint}
+        * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code>
+        * using the <code>&#964;</code>-adic NAF (TNAF) method, given the TNAF
+        * of <code>&#955;</code>.
+        * @param p The AbstractF2mPoint to Multiply.
+        * @param u The the TNAF of <code>&#955;</code>..
+        * @return <code>&#955; * p</code>
+        */
+        public static AbstractF2mPoint MultiplyFromTnaf(AbstractF2mPoint p, sbyte[] u)
+        {
+            ECCurve curve = p.Curve;
+            AbstractF2mPoint q = (AbstractF2mPoint)curve.Infinity;
+            AbstractF2mPoint pNeg = (AbstractF2mPoint)p.Negate();
+            int tauCount = 0;
+            for (int i = u.Length - 1; i >= 0; i--)
+            {
+                ++tauCount;
+                sbyte ui = u[i];
+                if (ui != 0)
+                {
+                    q = q.TauPow(tauCount);
+                    tauCount = 0;
+
+                    ECPoint x = ui > 0 ? p : pNeg;
+                    q = (AbstractF2mPoint)q.Add(x);
+                }
+            }
+            if (tauCount > 0)
+            {
+                q = q.TauPow(tauCount);
+            }
+            return q;
+        }
+
+        /**
+        * Computes the <code>[&#964;]</code>-adic window NAF of an element
+        * <code>&#955;</code> of <code><b>Z</b>[&#964;]</code>.
+        * @param mu The parameter &#956; of the elliptic curve.
+        * @param lambda The element <code>&#955;</code> of
+        * <code><b>Z</b>[&#964;]</code> of which to compute the
+        * <code>[&#964;]</code>-adic NAF.
+        * @param width The window width of the resulting WNAF.
+        * @param pow2w 2<sup>width</sup>.
+        * @param tw The auxiliary value <code>t<sub>w</sub></code>.
+        * @param alpha The <code>&#945;<sub>u</sub></code>'s for the window width.
+        * @return The <code>[&#964;]</code>-adic window NAF of
+        * <code>&#955;</code>.
+        */
+        public static sbyte[] TauAdicWNaf(sbyte mu, ZTauElement lambda,
+            sbyte width, BigInteger pow2w, BigInteger tw, ZTauElement[] alpha)
+        {
+            if (!((mu == 1) || (mu == -1))) 
+                throw new ArgumentException("mu must be 1 or -1");
+
+            BigInteger norm = Norm(mu, lambda);
+
+            // Ceiling of log2 of the norm 
+            int log2Norm = norm.BitLength;
+
+            // If length(TNAF) > 30, then length(TNAF) < log2Norm + 3.52
+            int maxLength = log2Norm > 30 ? log2Norm + 4 + width : 34 + width;
+
+            // The array holding the TNAF
+            sbyte[] u = new sbyte[maxLength];
+
+            // 2^(width - 1)
+            BigInteger pow2wMin1 = pow2w.ShiftRight(1);
+
+            // Split lambda into two BigIntegers to simplify calculations
+            BigInteger r0 = lambda.u;
+            BigInteger r1 = lambda.v;
+            int i = 0;
+
+            // while lambda <> (0, 0)
+            while (!((r0.Equals(BigInteger.Zero))&&(r1.Equals(BigInteger.Zero))))
+            {
+                // if r0 is odd
+                if (r0.TestBit(0)) 
+                {
+                    // uUnMod = r0 + r1*tw Mod 2^width
+                    BigInteger uUnMod
+                        = r0.Add(r1.Multiply(tw)).Mod(pow2w);
+                    
+                    sbyte uLocal;
+                    // if uUnMod >= 2^(width - 1)
+                    if (uUnMod.CompareTo(pow2wMin1) >= 0)
+                    {
+                        uLocal = (sbyte) uUnMod.Subtract(pow2w).IntValue;
+                    }
+                    else
+                    {
+                        uLocal = (sbyte) uUnMod.IntValue;
+                    }
+                    // uLocal is now in [-2^(width-1), 2^(width-1)-1]
+
+                    u[i] = uLocal;
+                    bool s = true;
+                    if (uLocal < 0) 
+                    {
+                        s = false;
+                        uLocal = (sbyte)-uLocal;
+                    }
+                    // uLocal is now >= 0
+
+                    if (s) 
+                    {
+                        r0 = r0.Subtract(alpha[uLocal].u);
+                        r1 = r1.Subtract(alpha[uLocal].v);
+                    }
+                    else
+                    {
+                        r0 = r0.Add(alpha[uLocal].u);
+                        r1 = r1.Add(alpha[uLocal].v);
+                    }
+                }
+                else
+                {
+                    u[i] = 0;
+                }
+
+                BigInteger t = r0;
+
+                if (mu == 1)
+                {
+                    r0 = r1.Add(r0.ShiftRight(1));
+                }
+                else
+                {
+                    // mu == -1
+                    r0 = r1.Subtract(r0.ShiftRight(1));
+                }
+                r1 = t.ShiftRight(1).Negate();
+                i++;
+            }
+            return u;
+        }
+
+        /**
+        * Does the precomputation for WTNAF multiplication.
+        * @param p The <code>ECPoint</code> for which to do the precomputation.
+        * @param a The parameter <code>a</code> of the elliptic curve.
+        * @return The precomputation array for <code>p</code>. 
+        */
+        public static AbstractF2mPoint[] GetPreComp(AbstractF2mPoint p, sbyte a)
+        {
+            sbyte[][] alphaTnaf = (a == 0) ? Tnaf.Alpha0Tnaf : Tnaf.Alpha1Tnaf;
+
+            AbstractF2mPoint[] pu = new AbstractF2mPoint[(uint)(alphaTnaf.Length + 1) >> 1];
+            pu[0] = p;
+
+            uint precompLen = (uint)alphaTnaf.Length;
+            for (uint i = 3; i < precompLen; i += 2)
+            {
+                pu[i >> 1] = Tnaf.MultiplyFromTnaf(p, alphaTnaf[i]);
+            }
+
+            p.Curve.NormalizeAll(pu);
+
+            return pu;
+        }
+    }
+}

+ 36 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/ZTauElement.cs

@@ -0,0 +1,36 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Abc
+{
+	/**
+	* Class representing an element of <code><b>Z</b>[&#964;]</code>. Let
+	* <code>&#955;</code> be an element of <code><b>Z</b>[&#964;]</code>. Then
+	* <code>&#955;</code> is given as <code>&#955; = u + v&#964;</code>. The
+	* components <code>u</code> and <code>v</code> may be used directly, there
+	* are no accessor methods.
+	* Immutable class.
+	*/
+	internal class ZTauElement 
+	{
+		/**
+		* The &quot;real&quot; part of <code>&#955;</code>.
+		*/
+		public readonly BigInteger u;
+
+		/**
+		* The &quot;<code>&#964;</code>-adic&quot; part of <code>&#955;</code>.
+		*/
+		public readonly BigInteger v;
+
+		/**
+		* Constructor for an element <code>&#955;</code> of
+		* <code><b>Z</b>[&#964;]</code>.
+		* @param u The &quot;real&quot; part of <code>&#955;</code>.
+		* @param v The &quot;<code>&#964;</code>-adic&quot; part of
+		* <code>&#955;</code>.
+		*/
+		public ZTauElement(BigInteger u, BigInteger v)
+		{
+			this.u = u;
+			this.v = v;
+		}
+	}
+}

+ 11 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/ECEndomorphism.cs

@@ -0,0 +1,11 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Endo
+{
+    internal interface ECEndomorphism
+    {
+        ECPointMap PointMap { get; }
+
+        bool HasEfficientPointMap { get; }
+    }
+}

+ 10 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Endo
+{
+    internal interface GlvEndomorphism
+        :   ECEndomorphism
+    {
+        BigInteger[] DecomposeScalar(BigInteger k);
+    }
+}

+ 29 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/AbstractECMultiplier.cs

@@ -0,0 +1,29 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    internal abstract class AbstractECMultiplier
+        : ECMultiplier
+    {
+        public virtual ECPoint Multiply(ECPoint p, BigInteger k)
+        {
+            int sign = k.SignValue;
+            if (sign == 0 || p.IsInfinity)
+                return p.Curve.Infinity;
+
+            ECPoint positive = MultiplyPositive(p, k.Abs());
+            ECPoint result = sign > 0 ? positive : positive.Negate();
+
+            /*
+             * Although the various multipliers ought not to produce invalid output under normal
+             * circumstances, a final check here is advised to guard against fault attacks.
+             */
+            return CheckResult(result);
+        }
+
+        protected abstract ECPoint MultiplyPositive(ECPoint p, BigInteger k);
+
+        protected virtual ECPoint CheckResult(ECPoint p)
+        {
+            return ECAlgorithms.ImplCheckResult(p);
+        }
+    }
+}

+ 18 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ECMultiplier.cs

@@ -0,0 +1,18 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    /**
+    * Interface for classes encapsulating a point multiplication algorithm
+    * for <code>ECPoint</code>s.
+    */
+    internal interface ECMultiplier
+    {
+        /**
+         * Multiplies the <code>ECPoint p</code> by <code>k</code>, i.e.
+         * <code>p</code> is added <code>k</code> times to itself.
+         * @param p The <code>ECPoint</code> to be multiplied.
+         * @param k The factor by which <code>p</code> is multiplied.
+         * @return <code>p</code> multiplied by <code>k</code>.
+         */
+        ECPoint Multiply(ECPoint p, BigInteger k);
+    }
+}

+ 58 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointCombMultiplier.cs

@@ -0,0 +1,58 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.Raw;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    internal class FixedPointCombMultiplier
+        : AbstractECMultiplier
+    {
+        protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k)
+        {
+            ECCurve c = p.Curve;
+            int size = FixedPointUtilities.GetCombSize(c);
+
+            if (k.BitLength > size)
+            {
+                /*
+                 * TODO The comb works best when the scalars are less than the (possibly unknown) order.
+                 * Still, if we want to handle larger scalars, we could allow customization of the comb
+                 * size, or alternatively we could deal with the 'extra' bits either by running the comb
+                 * multiple times as necessary, or by using an alternative multiplier as prelude.
+                 */
+                throw new InvalidOperationException("fixed-point comb doesn't support scalars larger than the curve order");
+            }
+
+            FixedPointPreCompInfo info = FixedPointUtilities.Precompute(p);
+            ECLookupTable lookupTable = info.LookupTable;
+            int width = info.Width;
+
+            int d = (size + width - 1) / width;
+
+            ECPoint R = c.Infinity;
+
+            int fullComb = d * width;
+            uint[] K = Nat.FromBigInteger(fullComb, k);
+
+            int top = fullComb - 1;
+            for (int i = 0; i < d; ++i)
+            {
+                uint secretIndex = 0;
+
+                for (int j = top - i; j >= 0; j -= d)
+                {
+                    uint secretBit = K[j >> 5] >> (j & 0x1F);
+                    secretIndex ^= secretBit >> 1;
+                    secretIndex <<= 1;
+                    secretIndex ^= secretBit;
+                }
+
+                ECPoint add = lookupTable.Lookup((int)secretIndex);
+
+                R = R.TwicePlus(add);
+            }
+
+            return R.Add(info.Offset);
+        }
+    }
+}

+ 43 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointPreCompInfo.cs

@@ -0,0 +1,43 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    /**
+     * Class holding precomputation data for fixed-point multiplications.
+     */
+    internal class FixedPointPreCompInfo
+        : PreCompInfo
+    {
+        protected ECPoint m_offset = null;
+
+        /**
+         * Lookup table for the precomputed <code>ECPoint</code>s used for a fixed point multiplication.
+         */
+        protected ECLookupTable m_lookupTable = null;
+
+        /**
+         * The width used for the precomputation. If a larger width precomputation
+         * is already available this may be larger than was requested, so calling
+         * code should refer to the actual width.
+         */
+        protected int m_width = -1;
+
+        public virtual ECLookupTable LookupTable
+        {
+            get { return m_lookupTable; }
+            set { this.m_lookupTable = value; }
+        }
+
+        public virtual ECPoint Offset
+        {
+			get { return m_offset; }
+			set { this.m_offset = value; }
+		}
+
+        public virtual int Width
+        {
+            get { return m_width; }
+            set { this.m_width = value; }
+        }
+    }
+}

+ 95 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointUtilities.cs

@@ -0,0 +1,95 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    internal class FixedPointUtilities
+    {
+        public static readonly string PRECOMP_NAME = "bc_fixed_point";
+
+        public static int GetCombSize(ECCurve c)
+        {
+            BigInteger order = c.Order;
+            return order == null ? c.FieldSize + 1 : order.BitLength;
+        }
+
+        public static FixedPointPreCompInfo GetFixedPointPreCompInfo(PreCompInfo preCompInfo)
+        {
+            return preCompInfo as FixedPointPreCompInfo;
+        }
+
+        public static FixedPointPreCompInfo Precompute(ECPoint p)
+        {
+            return (FixedPointPreCompInfo)p.Curve.Precompute(p, PRECOMP_NAME, new FixedPointCallback(p));
+        }
+
+        private class FixedPointCallback
+            : IPreCompCallback
+        {
+            private readonly ECPoint m_p;
+
+            internal FixedPointCallback(ECPoint p)
+            {
+                this.m_p = p;
+            }
+
+            public PreCompInfo Precompute(PreCompInfo existing)
+            {
+                FixedPointPreCompInfo existingFP = (existing is FixedPointPreCompInfo) ? (FixedPointPreCompInfo)existing : null;
+
+                ECCurve c = m_p.Curve;
+                int bits = FixedPointUtilities.GetCombSize(c);
+                int minWidth = bits > 250 ? 6 : 5;
+                int n = 1 << minWidth;
+
+                if (CheckExisting(existingFP, n))
+                    return existingFP;
+
+                int d = (bits + minWidth - 1) / minWidth;
+
+                ECPoint[] pow2Table = new ECPoint[minWidth + 1];
+                pow2Table[0] = m_p;
+                for (int i = 1; i < minWidth; ++i)
+                {
+                    pow2Table[i] = pow2Table[i - 1].TimesPow2(d);
+                }
+
+                // This will be the 'offset' value 
+                pow2Table[minWidth] = pow2Table[0].Subtract(pow2Table[1]);
+
+                c.NormalizeAll(pow2Table);
+
+                ECPoint[] lookupTable = new ECPoint[n];
+                lookupTable[0] = pow2Table[0];
+
+                for (int bit = minWidth - 1; bit >= 0; --bit)
+                {
+                    ECPoint pow2 = pow2Table[bit];
+
+                    int step = 1 << bit;
+                    for (int i = step; i < n; i += (step << 1))
+                    {
+                        lookupTable[i] = lookupTable[i - step].Add(pow2);
+                    }
+                }
+
+                c.NormalizeAll(lookupTable);
+
+                FixedPointPreCompInfo result = new FixedPointPreCompInfo();
+                result.LookupTable = c.CreateCacheSafeLookupTable(lookupTable, 0, lookupTable.Length);
+                result.Offset = pow2Table[minWidth];
+                result.Width = minWidth;
+                return result;
+            }
+
+            private bool CheckExisting(FixedPointPreCompInfo existingFP, int n)
+            {
+                return existingFP != null && CheckTable(existingFP.LookupTable, n);
+            }
+
+            private bool CheckTable(ECLookupTable table, int n)
+            {
+                return table != null && table.Size >= n;
+            }
+        }
+    }
+}

+ 40 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/GlvMultiplier.cs

@@ -0,0 +1,40 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Endo;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    internal class GlvMultiplier
+        :   AbstractECMultiplier
+    {
+        protected readonly ECCurve curve;
+        protected readonly GlvEndomorphism glvEndomorphism;
+
+        public GlvMultiplier(ECCurve curve, GlvEndomorphism glvEndomorphism)
+        {
+            if (curve == null || curve.Order == null)
+                throw new ArgumentException("Need curve with known group order", "curve");
+
+            this.curve = curve;
+            this.glvEndomorphism = glvEndomorphism;
+        }
+
+        protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k)
+        {
+            if (!curve.Equals(p.Curve))
+                throw new InvalidOperationException();
+
+            BigInteger n = p.Curve.Order;
+            BigInteger[] ab = glvEndomorphism.DecomposeScalar(k.Mod(n));
+            BigInteger a = ab[0], b = ab[1];
+
+            ECPointMap pointMap = glvEndomorphism.PointMap;
+            if (glvEndomorphism.HasEfficientPointMap)
+            {
+                return ECAlgorithms.ImplShamirsTrickWNaf(p, a, pointMap, b);
+            }
+
+            return ECAlgorithms.ImplShamirsTrickWNaf(p, a, pointMap.Map(p), b);
+        }
+    }
+}

+ 9 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/IPreCompCallback.cs

@@ -0,0 +1,9 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    internal interface IPreCompCallback
+    {
+        PreCompInfo Precompute(PreCompInfo existing);
+    }
+}

+ 11 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/PreCompInfo.cs

@@ -0,0 +1,11 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+	/**
+	* Interface for classes storing precomputation data for multiplication
+	* algorithms. Used as a Memento (see GOF patterns) for
+	* <code>WNafMultiplier</code>.
+	*/
+	internal interface PreCompInfo
+	{
+	}
+}

+ 44 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ValidityPreCompInfo.cs

@@ -0,0 +1,44 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    internal class ValidityPreCompInfo
+        : PreCompInfo
+    {
+        internal static readonly string PRECOMP_NAME = "bc_validity";
+
+        private bool failed = false;
+        private bool curveEquationPassed = false;
+        private bool orderPassed = false;
+
+        internal bool HasFailed()
+        {
+            return failed;
+        }
+
+        internal void ReportFailed()
+        {
+            failed = true;
+        }
+
+        internal bool HasCurveEquationPassed()
+        {
+            return curveEquationPassed;
+        }
+
+        internal void ReportCurveEquationPassed()
+        {
+            curveEquationPassed = true;
+        }
+
+        internal bool HasOrderPassed()
+        {
+            return orderPassed;
+        }
+
+        internal void ReportOrderPassed()
+        {
+            orderPassed = true;
+        }
+    }
+}

+ 98 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafL2RMultiplier.cs

@@ -0,0 +1,98 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    /**
+    * Class implementing the WNAF (Window Non-Adjacent Form) multiplication
+    * algorithm.
+    */
+    internal class WNafL2RMultiplier
+        : AbstractECMultiplier
+    {
+        /**
+         * Multiplies <code>this</code> by an integer <code>k</code> using the
+         * Window NAF method.
+         * @param k The integer by which <code>this</code> is multiplied.
+         * @return A new <code>ECPoint</code> which equals <code>this</code>
+         * multiplied by <code>k</code>.
+         */
+        protected override ECPoint MultiplyPositive(ECPoint p, BigInteger k)
+        {
+            // Clamp the window width in the range [2, 16]
+            int width = System.Math.Max(2, System.Math.Min(16, GetWindowSize(k.BitLength)));
+
+            WNafPreCompInfo wnafPreCompInfo = WNafUtilities.Precompute(p, width, true);
+            ECPoint[] preComp = wnafPreCompInfo.PreComp;
+            ECPoint[] preCompNeg = wnafPreCompInfo.PreCompNeg;
+
+            int[] wnaf = WNafUtilities.GenerateCompactWindowNaf(width, k);
+
+            ECPoint R = p.Curve.Infinity;
+
+            int i = wnaf.Length;
+
+            /*
+             * NOTE: We try to optimize the first window using the precomputed points to substitute an
+             * addition for 2 or more doublings.
+             */
+            if (i > 1)
+            {
+                int wi = wnaf[--i];
+                int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+                int n = System.Math.Abs(digit);
+                ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+
+                // Optimization can only be used for values in the lower half of the table
+                if ((n << 2) < (1 << width))
+                {
+                    int highest = LongArray.BitLengths[n];
+
+                    // TODO Get addition/doubling cost ratio from curve and compare to 'scale' to see if worth substituting?
+                    int scale = width - highest;
+                    int lowBits = n ^ (1 << (highest - 1));
+
+                    int i1 = ((1 << (width - 1)) - 1);
+                    int i2 = (lowBits << scale) + 1;
+                    R = table[i1 >> 1].Add(table[i2 >> 1]);
+
+                    zeroes -= scale;
+
+                    //Console.WriteLine("Optimized: 2^" + scale + " * " + n + " = " + i1 + " + " + i2);
+                }
+                else
+                {
+                    R = table[n >> 1];
+                }
+
+                R = R.TimesPow2(zeroes);
+            }
+
+            while (i > 0)
+            {
+                int wi = wnaf[--i];
+                int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+                int n = System.Math.Abs(digit);
+                ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+                ECPoint r = table[n >> 1];
+
+                R = R.TwicePlus(r);
+                R = R.TimesPow2(zeroes);
+            }
+
+            return R;
+        }
+
+        /**
+         * Determine window width to use for a scalar multiplication of the given size.
+         * 
+         * @param bits the bit-length of the scalar to multiply by
+         * @return the window size to use
+         */
+        protected virtual int GetWindowSize(int bits)
+        {
+            return WNafUtilities.GetWindowSize(bits);
+        }
+    }
+}

+ 46 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafPreCompInfo.cs

@@ -0,0 +1,46 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    /**
+    * Class holding precomputation data for the WNAF (Window Non-Adjacent Form)
+    * algorithm.
+    */
+    internal class WNafPreCompInfo
+        : PreCompInfo 
+    {
+        /**
+         * Array holding the precomputed <code>ECPoint</code>s used for a Window
+         * NAF multiplication.
+         */
+        protected ECPoint[] m_preComp = null;
+
+        /**
+         * Array holding the negations of the precomputed <code>ECPoint</code>s used
+         * for a Window NAF multiplication.
+         */
+        protected ECPoint[] m_preCompNeg = null;
+
+        /**
+         * Holds an <code>ECPoint</code> representing Twice(this). Used for the
+         * Window NAF multiplication to create or extend the precomputed values.
+         */
+        protected ECPoint m_twice = null;
+
+        public virtual ECPoint[] PreComp
+        {
+            get { return m_preComp; }
+            set { this.m_preComp = value; }
+        }
+
+        public virtual ECPoint[] PreCompNeg
+        {
+            get { return m_preCompNeg; }
+            set { this.m_preCompNeg = value; }
+        }
+
+        public virtual ECPoint Twice
+        {
+            get { return m_twice; }
+            set { this.m_twice = value; }
+        }
+    }
+}

+ 579 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafUtilities.cs

@@ -0,0 +1,579 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    internal abstract class WNafUtilities
+    {
+        public static readonly string PRECOMP_NAME = "bc_wnaf";
+
+        private static readonly int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 };
+
+        private static readonly ECPoint[] EMPTY_POINTS = new ECPoint[0];
+
+        public static int[] GenerateCompactNaf(BigInteger k)
+        {
+            if ((k.BitLength >> 16) != 0)
+                throw new ArgumentException("must have bitlength < 2^16", "k");
+            if (k.SignValue == 0)
+                return Arrays.EmptyInts;
+
+            BigInteger _3k = k.ShiftLeft(1).Add(k);
+
+            int bits = _3k.BitLength;
+            int[] naf = new int[bits >> 1];
+
+            BigInteger diff = _3k.Xor(k);
+
+            int highBit = bits - 1, length = 0, zeroes = 0;
+            for (int i = 1; i < highBit; ++i)
+            {
+                if (!diff.TestBit(i))
+                {
+                    ++zeroes;
+                    continue;
+                }
+
+                int digit = k.TestBit(i) ? -1 : 1;
+                naf[length++] = (digit << 16) | zeroes;
+                zeroes = 1;
+                ++i;
+            }
+
+            naf[length++] = (1 << 16) | zeroes;
+
+            if (naf.Length > length)
+            {
+                naf = Trim(naf, length);
+            }
+
+            return naf;
+        }
+
+        public static int[] GenerateCompactWindowNaf(int width, BigInteger k)
+        {
+            if (width == 2)
+            {
+                return GenerateCompactNaf(k);
+            }
+
+            if (width < 2 || width > 16)
+                throw new ArgumentException("must be in the range [2, 16]", "width");
+            if ((k.BitLength >> 16) != 0)
+                throw new ArgumentException("must have bitlength < 2^16", "k");
+            if (k.SignValue == 0)
+                return Arrays.EmptyInts;
+
+            int[] wnaf = new int[k.BitLength / width + 1];
+
+            // 2^width and a mask and sign bit set accordingly
+            int pow2 = 1 << width;
+            int mask = pow2 - 1;
+            int sign = pow2 >> 1;
+
+            bool carry = false;
+            int length = 0, pos = 0;
+
+            while (pos <= k.BitLength)
+            {
+                if (k.TestBit(pos) == carry)
+                {
+                    ++pos;
+                    continue;
+                }
+
+                k = k.ShiftRight(pos);
+
+                int digit = k.IntValue & mask;
+                if (carry)
+                {
+                    ++digit;
+                }
+
+                carry = (digit & sign) != 0;
+                if (carry)
+                {
+                    digit -= pow2;
+                }
+
+                int zeroes = length > 0 ? pos - 1 : pos;
+                wnaf[length++] = (digit << 16) | zeroes;
+                pos = width;
+            }
+
+            // Reduce the WNAF array to its actual length
+            if (wnaf.Length > length)
+            {
+                wnaf = Trim(wnaf, length);
+            }
+
+            return wnaf;
+        }
+
+        public static byte[] GenerateJsf(BigInteger g, BigInteger h)
+        {
+            int digits = System.Math.Max(g.BitLength, h.BitLength) + 1;
+            byte[] jsf = new byte[digits];
+
+            BigInteger k0 = g, k1 = h;
+            int j = 0, d0 = 0, d1 = 0;
+
+            int offset = 0;
+            while ((d0 | d1) != 0 || k0.BitLength > offset || k1.BitLength > offset)
+            {
+                int n0 = ((int)((uint)k0.IntValue >> offset) + d0) & 7;
+                int n1 = ((int)((uint)k1.IntValue >> offset) + d1) & 7;
+
+                int u0 = n0 & 1;
+                if (u0 != 0)
+                {
+                    u0 -= (n0 & 2);
+                    if ((n0 + u0) == 4 && (n1 & 3) == 2)
+                    {
+                        u0 = -u0;
+                    }
+                }
+
+                int u1 = n1 & 1;
+                if (u1 != 0)
+                {
+                    u1 -= (n1 & 2);
+                    if ((n1 + u1) == 4 && (n0 & 3) == 2)
+                    {
+                        u1 = -u1;
+                    }
+                }
+
+                if ((d0 << 1) == 1 + u0)
+                {
+                    d0 ^= 1;
+                }
+                if ((d1 << 1) == 1 + u1)
+                {
+                    d1 ^= 1;
+                }
+
+                if (++offset == 30)
+                {
+                    offset = 0;
+                    k0 = k0.ShiftRight(30);
+                    k1 = k1.ShiftRight(30);
+                }
+
+                jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF));
+            }
+
+            // Reduce the JSF array to its actual length
+            if (jsf.Length > j)
+            {
+                jsf = Trim(jsf, j);
+            }
+
+            return jsf;
+        }
+
+        public static byte[] GenerateNaf(BigInteger k)
+        {
+            if (k.SignValue == 0)
+                return Arrays.EmptyBytes;
+
+            BigInteger _3k = k.ShiftLeft(1).Add(k);
+
+            int digits = _3k.BitLength - 1;
+            byte[] naf = new byte[digits];
+
+            BigInteger diff = _3k.Xor(k);
+
+            for (int i = 1; i < digits; ++i)
+            {
+                if (diff.TestBit(i))
+                {
+                    naf[i - 1] = (byte)(k.TestBit(i) ? -1 : 1);
+                    ++i;
+                }
+            }
+
+            naf[digits - 1] = 1;
+
+            return naf;
+        }
+
+        /**
+         * Computes the Window NAF (non-adjacent Form) of an integer.
+         * @param width The width <code>w</code> of the Window NAF. The width is
+         * defined as the minimal number <code>w</code>, such that for any
+         * <code>w</code> consecutive digits in the resulting representation, at
+         * most one is non-zero.
+         * @param k The integer of which the Window NAF is computed.
+         * @return The Window NAF of the given width, such that the following holds:
+         * <code>k = &amp;sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
+         * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
+         * returned <code>byte[]</code>.
+         */
+        public static byte[] GenerateWindowNaf(int width, BigInteger k)
+        {
+            if (width == 2)
+            {
+                return GenerateNaf(k);
+            }
+
+            if (width < 2 || width > 8)
+                throw new ArgumentException("must be in the range [2, 8]", "width");
+            if (k.SignValue == 0)
+                return Arrays.EmptyBytes;
+
+            byte[] wnaf = new byte[k.BitLength + 1];
+
+            // 2^width and a mask and sign bit set accordingly
+            int pow2 = 1 << width;
+            int mask = pow2 - 1;
+            int sign = pow2 >> 1;
+
+            bool carry = false;
+            int length = 0, pos = 0;
+
+            while (pos <= k.BitLength)
+            {
+                if (k.TestBit(pos) == carry)
+                {
+                    ++pos;
+                    continue;
+                }
+
+                k = k.ShiftRight(pos);
+
+                int digit = k.IntValue & mask;
+                if (carry)
+                {
+                    ++digit;
+                }
+
+                carry = (digit & sign) != 0;
+                if (carry)
+                {
+                    digit -= pow2;
+                }
+
+                length += (length > 0) ? pos - 1 : pos;
+                wnaf[length++] = (byte)digit;
+                pos = width;
+            }
+
+            // Reduce the WNAF array to its actual length
+            if (wnaf.Length > length)
+            {
+                wnaf = Trim(wnaf, length);
+            }
+        
+            return wnaf;
+        }
+
+        public static int GetNafWeight(BigInteger k)
+        {
+            if (k.SignValue == 0)
+                return 0;
+
+            BigInteger _3k = k.ShiftLeft(1).Add(k);
+            BigInteger diff = _3k.Xor(k);
+
+            return diff.BitCount;
+        }
+
+        public static WNafPreCompInfo GetWNafPreCompInfo(ECPoint p)
+        {
+            return GetWNafPreCompInfo(p.Curve.GetPreCompInfo(p, PRECOMP_NAME));
+        }
+
+        public static WNafPreCompInfo GetWNafPreCompInfo(PreCompInfo preCompInfo)
+        {
+            return preCompInfo as WNafPreCompInfo;
+        }
+
+        /**
+         * Determine window width to use for a scalar multiplication of the given size.
+         * 
+         * @param bits the bit-length of the scalar to multiply by
+         * @return the window size to use
+         */
+        public static int GetWindowSize(int bits)
+        {
+            return GetWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS);
+        }
+
+        /**
+         * Determine window width to use for a scalar multiplication of the given size.
+         * 
+         * @param bits the bit-length of the scalar to multiply by
+         * @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width
+         * @return the window size to use
+         */
+        public static int GetWindowSize(int bits, int[] windowSizeCutoffs)
+        {
+            int w = 0;
+            for (; w < windowSizeCutoffs.Length; ++w)
+            {
+                if (bits < windowSizeCutoffs[w])
+                {
+                    break;
+                }
+            }
+            return w + 2;
+        }
+
+        public static ECPoint MapPointWithPrecomp(ECPoint p, int width, bool includeNegated,
+            ECPointMap pointMap)
+        {
+            ECCurve c = p.Curve;
+            WNafPreCompInfo wnafPreCompP = Precompute(p, width, includeNegated);
+
+            ECPoint q = pointMap.Map(p);
+            c.Precompute(q, PRECOMP_NAME, new MapPointCallback(wnafPreCompP, includeNegated, pointMap));
+            return q;
+        }
+
+        public static WNafPreCompInfo Precompute(ECPoint p, int width, bool includeNegated)
+        {
+            return (WNafPreCompInfo)p.Curve.Precompute(p, PRECOMP_NAME, new WNafCallback(p, width, includeNegated));
+        }
+
+        private static byte[] Trim(byte[] a, int length)
+        {
+            byte[] result = new byte[length];
+            Array.Copy(a, 0, result, 0, result.Length);
+            return result;
+        }
+
+        private static int[] Trim(int[] a, int length)
+        {
+            int[] result = new int[length];
+            Array.Copy(a, 0, result, 0, result.Length);
+            return result;
+        }
+
+        private static ECPoint[] ResizeTable(ECPoint[] a, int length)
+        {
+            ECPoint[] result = new ECPoint[length];
+            Array.Copy(a, 0, result, 0, a.Length);
+            return result;
+        }
+
+        private class MapPointCallback
+            : IPreCompCallback
+        {
+            private readonly WNafPreCompInfo m_wnafPreCompP;
+            private readonly bool m_includeNegated;
+            private readonly ECPointMap m_pointMap;
+
+            internal MapPointCallback(WNafPreCompInfo wnafPreCompP, bool includeNegated, ECPointMap pointMap)
+            {
+                this.m_wnafPreCompP = wnafPreCompP;
+                this.m_includeNegated = includeNegated;
+                this.m_pointMap = pointMap;
+            }
+
+            public PreCompInfo Precompute(PreCompInfo existing)
+            {
+                WNafPreCompInfo result = new WNafPreCompInfo();
+
+                ECPoint twiceP = m_wnafPreCompP.Twice;
+                if (twiceP != null)
+                {
+                    ECPoint twiceQ = m_pointMap.Map(twiceP);
+                    result.Twice = twiceQ;
+                }
+
+                ECPoint[] preCompP = m_wnafPreCompP.PreComp;
+                ECPoint[] preCompQ = new ECPoint[preCompP.Length];
+                for (int i = 0; i < preCompP.Length; ++i)
+                {
+                    preCompQ[i] = m_pointMap.Map(preCompP[i]);
+                }
+                result.PreComp = preCompQ;
+
+                if (m_includeNegated)
+                {
+                    ECPoint[] preCompNegQ = new ECPoint[preCompQ.Length];
+                    for (int i = 0; i < preCompNegQ.Length; ++i)
+                    {
+                        preCompNegQ[i] = preCompQ[i].Negate();
+                    }
+                    result.PreCompNeg = preCompNegQ;
+                }
+
+                return result;
+            }
+        }
+
+        private class WNafCallback
+            : IPreCompCallback
+        {
+            private readonly ECPoint m_p;
+            private readonly int m_width;
+            private readonly bool m_includeNegated;
+
+            internal WNafCallback(ECPoint p, int width, bool includeNegated)
+            {
+                this.m_p = p;
+                this.m_width = width;
+                this.m_includeNegated = includeNegated;
+            }
+
+            public PreCompInfo Precompute(PreCompInfo existing)
+            {
+                WNafPreCompInfo existingWNaf = existing as WNafPreCompInfo;
+
+                int reqPreCompLen = 1 << System.Math.Max(0, m_width - 2);
+
+                if (CheckExisting(existingWNaf, reqPreCompLen, m_includeNegated))
+                    return existingWNaf;
+
+                ECCurve c = m_p.Curve;
+                ECPoint[] preComp = null, preCompNeg = null;
+                ECPoint twiceP = null;
+
+                if (existingWNaf != null)
+                {
+                    preComp = existingWNaf.PreComp;
+                    preCompNeg = existingWNaf.PreCompNeg;
+                    twiceP = existingWNaf.Twice;
+                }
+
+                int iniPreCompLen = 0;
+                if (preComp == null)
+                {
+                    preComp = EMPTY_POINTS;
+                }
+                else
+                {
+                    iniPreCompLen = preComp.Length;
+                }
+
+                if (iniPreCompLen < reqPreCompLen)
+                {
+                    preComp = WNafUtilities.ResizeTable(preComp, reqPreCompLen);
+
+                    if (reqPreCompLen == 1)
+                    {
+                        preComp[0] = m_p.Normalize();
+                    }
+                    else
+                    {
+                        int curPreCompLen = iniPreCompLen;
+                        if (curPreCompLen == 0)
+                        {
+                            preComp[0] = m_p;
+                            curPreCompLen = 1;
+                        }
+
+                        ECFieldElement iso = null;
+
+                        if (reqPreCompLen == 2)
+                        {
+                            preComp[1] = m_p.ThreeTimes();
+                        }
+                        else
+                        {
+                            ECPoint isoTwiceP = twiceP, last = preComp[curPreCompLen - 1];
+                            if (isoTwiceP == null)
+                            {
+                                isoTwiceP = preComp[0].Twice();
+                                twiceP = isoTwiceP;
+
+                                /*
+                                 * For Fp curves with Jacobian projective coordinates, use a (quasi-)isomorphism
+                                 * where 'twiceP' is "affine", so that the subsequent additions are cheaper. This
+                                 * also requires scaling the initial point's X, Y coordinates, and reversing the
+                                 * isomorphism as part of the subsequent normalization.
+                                 * 
+                                 *  NOTE: The correctness of this optimization depends on:
+                                 *      1) additions do not use the curve's A, B coefficients.
+                                 *      2) no special cases (i.e. Q +/- Q) when calculating 1P, 3P, 5P, ...
+                                 */
+                                if (!twiceP.IsInfinity && ECAlgorithms.IsFpCurve(c) && c.FieldSize >= 64)
+                                {
+                                    switch (c.CoordinateSystem)
+                                    {
+                                    case ECCurve.COORD_JACOBIAN:
+                                    case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+                                    case ECCurve.COORD_JACOBIAN_MODIFIED:
+                                    {
+                                        iso = twiceP.GetZCoord(0);
+                                        isoTwiceP = c.CreatePoint(twiceP.XCoord.ToBigInteger(),
+                                            twiceP.YCoord.ToBigInteger());
+
+                                        ECFieldElement iso2 = iso.Square(), iso3 = iso2.Multiply(iso);
+                                        last = last.ScaleX(iso2).ScaleY(iso3);
+
+                                        if (iniPreCompLen == 0)
+                                        {
+                                            preComp[0] = last;
+                                        }
+                                        break;
+                                    }
+                                    }
+                                }
+                            }
+
+                            while (curPreCompLen < reqPreCompLen)
+                            {
+                                /*
+                                 * Compute the new ECPoints for the precomputation array. The values 1, 3,
+                                 * 5, ..., 2^(width-1)-1 times p are computed
+                                 */
+                                preComp[curPreCompLen++] = last = last.Add(isoTwiceP);
+                            }
+                        }
+
+                        /*
+                         * Having oft-used operands in affine form makes operations faster.
+                         */
+                        c.NormalizeAll(preComp, iniPreCompLen, reqPreCompLen - iniPreCompLen, iso);
+                    }
+                }
+
+                if (m_includeNegated)
+                {
+                    int pos;
+                    if (preCompNeg == null)
+                    {
+                        pos = 0;
+                        preCompNeg = new ECPoint[reqPreCompLen]; 
+                    }
+                    else
+                    {
+                        pos = preCompNeg.Length;
+                        if (pos < reqPreCompLen)
+                        {
+                            preCompNeg = WNafUtilities.ResizeTable(preCompNeg, reqPreCompLen);
+                        }
+                    }
+
+                    while (pos < reqPreCompLen)
+                    {
+                        preCompNeg[pos] = preComp[pos].Negate();
+                        ++pos;
+                    }
+                }
+
+                WNafPreCompInfo result = new WNafPreCompInfo();
+                result.PreComp = preComp;
+                result.PreCompNeg = preCompNeg;
+                result.Twice = twiceP;
+                return result;
+            }
+
+            private bool CheckExisting(WNafPreCompInfo existingWNaf, int reqPreCompLen, bool includeNegated)
+            {
+                return existingWNaf != null
+                    && CheckTable(existingWNaf.PreComp, reqPreCompLen)
+                    && (!includeNegated || CheckTable(existingWNaf.PreCompNeg, reqPreCompLen));
+            }
+
+            private bool CheckTable(ECPoint[] table, int reqLen)
+            {
+                return table != null && table.Length >= reqLen;
+            }
+        }
+    }
+}

+ 138 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafMultiplier.cs

@@ -0,0 +1,138 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Abc;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    /**
+    * Class implementing the WTNAF (Window
+    * <code>&#964;</code>-adic Non-Adjacent Form) algorithm.
+    */
+    internal class WTauNafMultiplier
+        : AbstractECMultiplier
+    {
+        // TODO Create WTauNafUtilities class and move various functionality into it
+        internal static readonly string PRECOMP_NAME = "bc_wtnaf";
+
+        /**
+        * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint}
+        * by <code>k</code> using the reduced <code>&#964;</code>-adic NAF (RTNAF)
+        * method.
+        * @param p The AbstractF2mPoint to multiply.
+        * @param k The integer by which to multiply <code>k</code>.
+        * @return <code>p</code> multiplied by <code>k</code>.
+        */
+        protected override ECPoint MultiplyPositive(ECPoint point, BigInteger k)
+        {
+            if (!(point is AbstractF2mPoint))
+                throw new ArgumentException("Only AbstractF2mPoint can be used in WTauNafMultiplier");
+
+            AbstractF2mPoint p = (AbstractF2mPoint)point;
+            AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve;
+            int m = curve.FieldSize;
+            sbyte a = (sbyte)curve.A.ToBigInteger().IntValue;
+            sbyte mu = Tnaf.GetMu(a);
+            BigInteger[] s = curve.GetSi();
+
+            ZTauElement rho = Tnaf.PartModReduction(k, m, a, s, mu, (sbyte)10);
+
+            return MultiplyWTnaf(p, rho, a, mu);
+        }
+
+        /**
+        * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint}
+        * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code> using
+        * the <code>&#964;</code>-adic NAF (TNAF) method.
+        * @param p The AbstractF2mPoint to multiply.
+        * @param lambda The element <code>&#955;</code> of
+        * <code><b>Z</b>[&#964;]</code> of which to compute the
+        * <code>[&#964;]</code>-adic NAF.
+        * @return <code>p</code> multiplied by <code>&#955;</code>.
+        */
+        private AbstractF2mPoint MultiplyWTnaf(AbstractF2mPoint p, ZTauElement lambda,
+            sbyte a, sbyte mu)
+        {
+            ZTauElement[] alpha = (a == 0) ? Tnaf.Alpha0 : Tnaf.Alpha1;
+
+            BigInteger tw = Tnaf.GetTw(mu, Tnaf.Width);
+
+            sbyte[]u = Tnaf.TauAdicWNaf(mu, lambda, Tnaf.Width,
+                BigInteger.ValueOf(Tnaf.Pow2Width), tw, alpha);
+
+            return MultiplyFromWTnaf(p, u);
+        }
+        
+        /**
+        * Multiplies a {@link org.bouncycastle.math.ec.AbstractF2mPoint AbstractF2mPoint}
+        * by an element <code>&#955;</code> of <code><b>Z</b>[&#964;]</code>
+        * using the window <code>&#964;</code>-adic NAF (TNAF) method, given the
+        * WTNAF of <code>&#955;</code>.
+        * @param p The AbstractF2mPoint to multiply.
+        * @param u The the WTNAF of <code>&#955;</code>..
+        * @return <code>&#955; * p</code>
+        */
+        private static AbstractF2mPoint MultiplyFromWTnaf(AbstractF2mPoint p, sbyte[] u)
+        {
+            AbstractF2mCurve curve = (AbstractF2mCurve)p.Curve;
+            sbyte a = (sbyte)curve.A.ToBigInteger().IntValue;
+
+            WTauNafCallback callback = new WTauNafCallback(p, a);
+            WTauNafPreCompInfo preCompInfo = (WTauNafPreCompInfo)curve.Precompute(p, PRECOMP_NAME, callback);
+            AbstractF2mPoint[] pu = preCompInfo.PreComp;
+
+            // TODO Include negations in precomp (optionally) and use from here
+            AbstractF2mPoint[] puNeg = new AbstractF2mPoint[pu.Length];
+            for (int i = 0; i < pu.Length; ++i)
+            {
+                puNeg[i] = (AbstractF2mPoint)pu[i].Negate();
+            }
+
+            
+            // q = infinity
+            AbstractF2mPoint q = (AbstractF2mPoint) p.Curve.Infinity;
+
+            int tauCount = 0;
+            for (int i = u.Length - 1; i >= 0; i--)
+            {
+                ++tauCount;
+                int ui = u[i];
+                if (ui != 0)
+                {
+                    q = q.TauPow(tauCount);
+                    tauCount = 0;
+
+                    ECPoint x = ui > 0 ? pu[ui >> 1] : puNeg[(-ui) >> 1];
+                    q = (AbstractF2mPoint)q.Add(x);
+                }
+            }
+            if (tauCount > 0)
+            {
+                q = q.TauPow(tauCount);
+            }
+            return q;
+        }
+
+        private class WTauNafCallback
+            : IPreCompCallback
+        {
+            private readonly AbstractF2mPoint m_p;
+            private readonly sbyte m_a;
+
+            internal WTauNafCallback(AbstractF2mPoint p, sbyte a)
+            {
+                this.m_p = p;
+                this.m_a = a;
+            }
+
+            public PreCompInfo Precompute(PreCompInfo existing)
+            {
+                if (existing is WTauNafPreCompInfo)
+                    return existing;
+
+                WTauNafPreCompInfo result = new WTauNafPreCompInfo();
+                result.PreComp = Tnaf.GetPreComp(m_p, m_a);
+                return result;
+            }
+        }
+    }
+}

+ 24 - 0
src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafPreCompInfo.cs

@@ -0,0 +1,24 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.EC.Multiplier
+{
+    /**
+     * Class holding precomputation data for the WTNAF (Window
+     * <code>&#964;</code>-adic Non-Adjacent Form) algorithm.
+     */
+    internal class WTauNafPreCompInfo
+        : PreCompInfo
+    {
+        /**
+         * Array holding the precomputed <code>AbstractF2mPoint</code>s used for the
+         * WTNAF multiplication in <code>
+         * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply()
+         * WTauNafMultiplier.multiply()}</code>.
+         */
+        protected AbstractF2mPoint[] m_preComp;
+
+        public virtual AbstractF2mPoint[] PreComp
+        {
+            get { return m_preComp; }
+            set { this.m_preComp = value; }
+        }
+    }
+}

+ 54 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/FiniteFields.cs

@@ -0,0 +1,54 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal abstract class FiniteFields
+    {
+        internal static readonly IFiniteField GF_2 = new PrimeField(BigInteger.ValueOf(2));
+        internal static readonly IFiniteField GF_3 = new PrimeField(BigInteger.ValueOf(3));
+
+        public static IPolynomialExtensionField GetBinaryExtensionField(int[] exponents)
+        {
+            if (exponents[0] != 0)
+            {
+                throw new ArgumentException("Irreducible polynomials in GF(2) must have constant term", "exponents");
+            }
+            for (int i = 1; i < exponents.Length; ++i)
+            {
+                if (exponents[i] <= exponents[i - 1])
+                {
+                    throw new ArgumentException("Polynomial exponents must be montonically increasing", "exponents");
+                }
+            }
+
+            return new GenericPolynomialExtensionField(GF_2, new GF2Polynomial(exponents));
+        }
+
+    //    public static IPolynomialExtensionField GetTernaryExtensionField(Term[] terms)
+    //    {
+    //        return new GenericPolynomialExtensionField(GF_3, new GF3Polynomial(terms));
+    //    }
+
+        public static IFiniteField GetPrimeField(BigInteger characteristic)
+        {
+            int bitLength = characteristic.BitLength;
+            if (characteristic.SignValue <= 0 || bitLength < 2)
+            {
+                throw new ArgumentException("Must be >= 2", "characteristic");
+            }
+
+            if (bitLength < 3)
+            {
+                switch (characteristic.IntValue)
+                {
+                case 2:
+                    return GF_2;
+                case 3:
+                    return GF_3;
+                }
+            }
+
+            return new PrimeField(characteristic);
+        }
+    }
+}

+ 46 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/GF2Polynomial.cs

@@ -0,0 +1,46 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal class GF2Polynomial
+        : IPolynomial
+    {
+        protected readonly int[] exponents;
+
+        internal GF2Polynomial(int[] exponents)
+        {
+            this.exponents = Arrays.Clone(exponents);
+        }
+
+        public virtual int Degree
+        {
+            get { return exponents[exponents.Length - 1]; }
+        }
+
+        public virtual int[] GetExponentsPresent()
+        {
+            return Arrays.Clone(exponents);
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (this == obj)
+            {
+                return true;
+            }
+            GF2Polynomial other = obj as GF2Polynomial;
+            if (null == other)
+            {
+                return false;
+            }
+            return Arrays.AreEqual(exponents, other.exponents);
+        }
+
+        public override int GetHashCode()
+        {
+            return Arrays.GetHashCode(exponents);
+        }
+    }
+}

+ 63 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/GenericPolynomialExtensionField.cs

@@ -0,0 +1,63 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal class GenericPolynomialExtensionField
+        : IPolynomialExtensionField
+    {
+        protected readonly IFiniteField subfield;
+        protected readonly IPolynomial minimalPolynomial;
+
+        internal GenericPolynomialExtensionField(IFiniteField subfield, IPolynomial polynomial)
+        {
+            this.subfield = subfield;
+            this.minimalPolynomial = polynomial;
+        }
+
+        public virtual BigInteger Characteristic
+        {
+            get { return subfield.Characteristic; }
+        }
+
+        public virtual int Dimension
+        {
+            get { return subfield.Dimension * minimalPolynomial.Degree; }
+        }
+
+        public virtual IFiniteField Subfield
+        {
+            get { return subfield; }
+        }
+
+        public virtual int Degree
+        {
+            get { return minimalPolynomial.Degree; }
+        }
+
+        public virtual IPolynomial MinimalPolynomial
+        {
+            get { return minimalPolynomial; }
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (this == obj)
+            {
+                return true;
+            }
+            GenericPolynomialExtensionField other = obj as GenericPolynomialExtensionField;
+            if (null == other)
+            {
+                return false;
+            }
+            return subfield.Equals(other.subfield) && minimalPolynomial.Equals(other.minimalPolynomial);
+        }
+
+        public override int GetHashCode()
+        {
+            return subfield.GetHashCode() ^ Integers.RotateLeft(minimalPolynomial.GetHashCode(), 16);
+        }
+    }
+}

+ 12 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/IExtensionField.cs

@@ -0,0 +1,12 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal interface IExtensionField
+        : IFiniteField
+    {
+        IFiniteField Subfield { get; }
+
+        int Degree { get; }
+    }
+}

+ 11 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/IFiniteField.cs

@@ -0,0 +1,11 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal interface IFiniteField
+    {
+        BigInteger Characteristic { get; }
+
+        int Dimension { get; }
+    }
+}

+ 13 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomial.cs

@@ -0,0 +1,13 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal interface IPolynomial
+    {
+        int Degree { get; }
+
+        //BigInteger[] GetCoefficients();
+
+        int[] GetExponentsPresent();
+
+        //Term[] GetNonZeroTerms();
+    }
+}

+ 8 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomialExtensionField.cs

@@ -0,0 +1,8 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal interface IPolynomialExtensionField
+        : IExtensionField
+    {
+        IPolynomial MinimalPolynomial { get; }
+    }
+}

+ 42 - 0
src/Renci.SshNet/Security/BouncyCastle/math/field/PrimeField.cs

@@ -0,0 +1,42 @@
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Field
+{
+    internal class PrimeField
+        : IFiniteField
+    {
+        protected readonly BigInteger characteristic;
+
+        internal PrimeField(BigInteger characteristic)
+        {
+            this.characteristic = characteristic;
+        }
+
+        public virtual BigInteger Characteristic
+        {
+            get { return characteristic; }
+        }
+
+        public virtual int Dimension
+        {
+            get { return 1; }
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (this == obj)
+            {
+                return true;
+            }
+            PrimeField other = obj as PrimeField;
+            if (null == other)
+            {
+                return false;
+            }
+            return characteristic.Equals(other.characteristic);
+        }
+
+        public override int GetHashCode()
+        {
+            return characteristic.GetHashCode();
+        }
+    }
+}

+ 185 - 0
src/Renci.SshNet/Security/BouncyCastle/math/raw/Mod.cs

@@ -0,0 +1,185 @@
+using System;
+using System.Diagnostics;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Utilities;
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Raw
+{
+    internal abstract class Mod
+    {
+        private static readonly SecureRandom RandomSource = new SecureRandom();
+
+        public static void Invert(uint[] p, uint[] x, uint[] z)
+        {
+            int len = p.Length;
+            if (Nat.IsZero(len, x))
+                throw new ArgumentException("cannot be 0", "x");
+            if (Nat.IsOne(len, x))
+            {
+                Array.Copy(x, 0, z, 0, len);
+                return;
+            }
+
+            uint[] u = Nat.Copy(len, x);
+            uint[] a = Nat.Create(len);
+            a[0] = 1;
+            int ac = 0;
+
+            if ((u[0] & 1) == 0)
+            {
+                InversionStep(p, u, len, a, ref ac);
+            }
+            if (Nat.IsOne(len, u))
+            {
+                InversionResult(p, ac, a, z);
+                return;
+            }
+
+            uint[] v = Nat.Copy(len, p);
+            uint[] b = Nat.Create(len);
+            int bc = 0;
+
+            int uvLen = len;
+
+            for (;;)
+            {
+                while (u[uvLen - 1] == 0 && v[uvLen - 1] == 0)
+                {
+                    --uvLen;
+                }
+
+                if (Nat.Gte(len, u, v))
+                {
+                    Nat.SubFrom(len, v, u);
+                    Debug.Assert((u[0] & 1) == 0);
+                    ac += Nat.SubFrom(len, b, a) - bc;
+                    InversionStep(p, u, uvLen, a, ref ac);
+                    if (Nat.IsOne(len, u))
+                    {
+                        InversionResult(p, ac, a, z);
+                        return;
+                    }
+                }
+                else
+                {
+                    Nat.SubFrom(len, u, v);
+                    Debug.Assert((v[0] & 1) == 0);
+                    bc += Nat.SubFrom(len, a, b) - ac;
+                    InversionStep(p, v, uvLen, b, ref bc);
+                    if (Nat.IsOne(len, v))
+                    {
+                        InversionResult(p, bc, b, z);
+                        return;
+                    }
+                }
+            }
+        }
+
+        public static uint[] Random(uint[] p)
+        {
+            int len = p.Length;
+            uint[] s = Nat.Create(len);
+
+            uint m = p[len - 1];
+            m |= m >> 1;
+            m |= m >> 2;
+            m |= m >> 4;
+            m |= m >> 8;
+            m |= m >> 16;
+
+            do
+            {
+                byte[] bytes = new byte[len << 2];
+                RandomSource.NextBytes(bytes);
+                Pack.BE_To_UInt32(bytes, 0, s);
+                s[len - 1] &= m;
+            }
+            while (Nat.Gte(len, s, p));
+
+            return s;
+        }
+
+        public static void Add(uint[] p, uint[] x, uint[] y, uint[] z)
+        {
+            int len = p.Length;
+            uint c = Nat.Add(len, x, y, z);
+            if (c != 0)
+            {
+                Nat.SubFrom(len, p, z);
+            }
+        }
+
+        public static void Subtract(uint[] p, uint[] x, uint[] y, uint[] z)
+        {
+            int len = p.Length;
+            int c = Nat.Sub(len, x, y, z);
+            if (c != 0)
+            {
+                Nat.AddTo(len, p, z);
+            }
+        }
+
+        private static void InversionResult(uint[] p, int ac, uint[] a, uint[] z)
+        {
+            if (ac < 0)
+            {
+                Nat.Add(p.Length, a, p, z);
+            }
+            else
+            {
+                Array.Copy(a, 0, z, 0, p.Length);
+            }
+        }
+
+        private static void InversionStep(uint[] p, uint[] u, int uLen, uint[] x, ref int xc)
+        {
+            int len = p.Length;
+            int count = 0;
+            while (u[0] == 0)
+            {
+                Nat.ShiftDownWord(uLen, u, 0);
+                count += 32;
+            }
+
+            {
+                int zeroes = GetTrailingZeroes(u[0]);
+                if (zeroes > 0)
+                {
+                    Nat.ShiftDownBits(uLen, u, zeroes, 0);
+                    count += zeroes;
+                }
+            }
+
+            for (int i = 0; i < count; ++i)
+            {
+                if ((x[0] & 1) != 0)
+                {
+                    if (xc < 0)
+                    {
+                        xc += (int)Nat.AddTo(len, p, x);
+                    }
+                    else
+                    {
+                        xc += Nat.SubFrom(len, p, x);
+                    }
+                }
+
+                Debug.Assert(xc == 0 || xc == -1);
+                Nat.ShiftDownBit(len, x, (uint)xc);
+            }
+        }
+
+        private static int GetTrailingZeroes(uint x)
+        {
+            Debug.Assert(x != 0);
+            int count = 0;
+            while ((x & 1) == 0)
+            {
+                x >>= 1;
+                ++count;
+            }
+            return count;
+        }
+    }
+}

+ 1153 - 0
src/Renci.SshNet/Security/BouncyCastle/math/raw/Nat.cs

@@ -0,0 +1,1153 @@
+using System;
+using System.Diagnostics;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Math.Raw
+{
+    internal abstract class Nat
+    {
+        private const ulong M = 0xFFFFFFFFUL;
+
+        public static uint Add(int len, uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (ulong)x[i] + y[i];
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            return (uint)c;
+        }
+
+        public static uint Add33At(int len, uint x, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            ulong c = (ulong)z[zPos + 0] + x;
+            z[zPos + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[zPos + 1] + 1;
+            z[zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zPos + 2);
+        }
+
+        public static uint Add33At(int len, uint x, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            ulong c = (ulong)z[zOff + zPos] + x;
+            z[zOff + zPos] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[zOff + zPos + 1] + 1;
+            z[zOff + zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zOff, zPos + 2);
+        }
+
+        public static uint Add33To(int len, uint x, uint[] z)
+        {
+            ulong c = (ulong)z[0] + x;
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[1] + 1;
+            z[1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, 2);
+        }
+
+        public static uint Add33To(int len, uint x, uint[] z, int zOff)
+        {
+            ulong c = (ulong)z[zOff + 0] + x;
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[zOff + 1] + 1;
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zOff, 2);
+        }
+
+        public static uint AddBothTo(int len, uint[] x, uint[] y, uint[] z)
+        {
+            ulong c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (ulong)x[i] + y[i] + z[i];
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            return (uint)c;
+        }
+
+        public static uint AddBothTo(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (ulong)x[xOff + i] + y[yOff + i] + z[zOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            return (uint)c;
+        }
+
+        public static uint AddDWordAt(int len, ulong x, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            ulong c = (ulong)z[zPos + 0] + (x & M);
+            z[zPos + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[zPos + 1] + (x >> 32);
+            z[zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zPos + 2);
+        }
+
+        public static uint AddDWordAt(int len, ulong x, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            ulong c = (ulong)z[zOff + zPos] + (x & M);
+            z[zOff + zPos] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[zOff + zPos + 1] + (x >> 32);
+            z[zOff + zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zOff, zPos + 2);
+        }
+
+        public static uint AddDWordTo(int len, ulong x, uint[] z)
+        {
+            ulong c = (ulong)z[0] + (x & M);
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[1] + (x >> 32);
+            z[1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, 2);
+        }
+
+        public static uint AddDWordTo(int len, ulong x, uint[] z, int zOff)
+        {
+            ulong c = (ulong)z[zOff + 0] + (x & M);
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[zOff + 1] + (x >> 32);
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zOff, 2);
+        }
+
+        public static uint AddTo(int len, uint[] x, uint[] z)
+        {
+            ulong c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (ulong)x[i] + z[i];
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            return (uint)c;
+        }
+
+        public static uint AddTo(int len, uint[] x, int xOff, uint[] z, int zOff)
+        {
+            ulong c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (ulong)x[xOff + i] + z[zOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            return (uint)c;
+        }
+
+        public static uint AddWordAt(int len, uint x, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 1));
+            ulong c = (ulong)x + z[zPos];
+            z[zPos] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zPos + 1);
+        }
+
+        public static uint AddWordAt(int len, uint x, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 1));
+            ulong c = (ulong)x + z[zOff + zPos];
+            z[zOff + zPos] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zOff, zPos + 1);
+        }
+
+        public static uint AddWordTo(int len, uint x, uint[] z)
+        {
+            ulong c = (ulong)x + z[0];
+            z[0] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, 1);
+        }
+
+        public static uint AddWordTo(int len, uint x, uint[] z, int zOff)
+        {
+            ulong c = (ulong)x + z[zOff];
+            z[zOff] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zOff, 1);
+        }
+
+        public static uint CAdd(int len, int mask, uint[] x, uint[] y, uint[] z)
+        {
+            uint MASK = (uint)-(mask & 1);
+
+            ulong c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (ulong)x[i] + (y[i] & MASK);
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            return (uint)c;
+        }
+
+        public static void CMov(int len, int mask, uint[] x, int xOff, uint[] z, int zOff)
+        {
+            uint MASK = (uint)-(mask & 1);
+
+            for (int i = 0; i < len; ++i)
+            {
+                uint z_i = z[zOff + i], diff = z_i ^ x[xOff + i];
+                z_i ^= (diff & MASK);
+                z[zOff + i] = z_i;
+            }
+
+            //uint half = 0x55555555U, rest = half << (-(int)MASK);
+
+            //for (int i = 0; i < len; ++i)
+            //{
+            //    uint z_i = z[zOff + i], diff = z_i ^ x[xOff + i];
+            //    z_i ^= (diff & half);
+            //    z_i ^= (diff & rest);
+            //    z[zOff + i] = z_i;
+            //}
+        }
+
+        public static void CMov(int len, int mask, int[] x, int xOff, int[] z, int zOff)
+        {
+            mask = -(mask & 1);
+
+            for (int i = 0; i < len; ++i)
+            {
+                int z_i = z[zOff + i], diff = z_i ^ x[xOff + i];
+                z_i ^= (diff & mask);
+                z[zOff + i] = z_i;
+            }
+
+            //int half = 0x55555555, rest = half << (-mask);
+
+            //for (int i = 0; i < len; ++i)
+            //{
+            //    int z_i = z[zOff + i], diff = z_i ^ x[xOff + i];
+            //    z_i ^= (diff & half);
+            //    z_i ^= (diff & rest);
+            //    z[zOff + i] = z_i;
+            //}
+        }
+
+        public static void Copy(int len, uint[] x, uint[] z)
+        {
+            Array.Copy(x, 0, z, 0, len);
+        }
+
+        public static uint[] Copy(int len, uint[] x)
+        {
+            uint[] z = new uint[len];
+            Array.Copy(x, 0, z, 0, len);
+            return z;
+        }
+
+        public static void Copy(int len, uint[] x, int xOff, uint[] z, int zOff)
+        {
+            Array.Copy(x, xOff, z, zOff, len);
+        }
+
+        public static uint[] Create(int len)
+        {
+            return new uint[len];
+        }
+
+        public static ulong[] Create64(int len)
+        {
+            return new ulong[len];
+        }
+
+        public static int Dec(int len, uint[] z)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                if (--z[i] != uint.MaxValue)
+                {
+                    return 0;
+                }
+            }
+            return -1;
+        }
+
+        public static int Dec(int len, uint[] x, uint[] z)
+        {
+            int i = 0;
+            while (i < len)
+            {
+                uint c = x[i] - 1;
+                z[i] = c;
+                ++i;
+                if (c != uint.MaxValue)
+                {
+                    while (i < len)
+                    {
+                        z[i] = x[i];
+                        ++i;
+                    }
+                    return 0;
+                }
+            }
+            return -1;
+        }
+
+        public static int DecAt(int len, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= len);
+            for (int i = zPos; i < len; ++i)
+            {
+                if (--z[i] != uint.MaxValue)
+                {
+                    return 0;
+                }
+            }
+            return -1;
+        }
+
+        public static int DecAt(int len, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= len);
+            for (int i = zPos; i < len; ++i)
+            {
+                if (--z[zOff + i] != uint.MaxValue)
+                {
+                    return 0;
+                }
+            }
+            return -1;
+        }
+
+        public static bool Eq(int len, uint[] x, uint[] y)
+        {
+            for (int i = len - 1; i >= 0; --i)
+            {
+                if (x[i] != y[i])
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static uint[] FromBigInteger(int bits, BigInteger x)
+        {
+            if (x.SignValue < 0 || x.BitLength > bits)
+                throw new ArgumentException();
+
+            int len = (bits + 31) >> 5;
+            uint[] z = Create(len);
+            int i = 0;
+            while (x.SignValue != 0)
+            {
+                z[i++] = (uint)x.IntValue;
+                x = x.ShiftRight(32);
+            }
+            return z;
+        }
+
+        public static uint GetBit(uint[] x, int bit)
+        {
+            if (bit == 0)
+            {
+                return x[0] & 1;
+            }
+            int w = bit >> 5;
+            if (w < 0 || w >= x.Length)
+            {
+                return 0;
+            }
+            int b = bit & 31;
+            return (x[w] >> b) & 1;
+        }
+
+        public static bool Gte(int len, uint[] x, uint[] y)
+        {
+            for (int i = len - 1; i >= 0; --i)
+            {
+                uint x_i = x[i], y_i = y[i];
+                if (x_i < y_i)
+                    return false;
+                if (x_i > y_i)
+                    return true;
+            }
+            return true;
+        }
+
+        public static uint Inc(int len, uint[] z)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                if (++z[i] != uint.MinValue)
+                {
+                    return 0;
+                }
+            }
+            return 1;
+        }
+
+        public static uint Inc(int len, uint[] x, uint[] z)
+        {
+            int i = 0;
+            while (i < len)
+            {
+                uint c = x[i] + 1;
+                z[i] = c;
+                ++i;
+                if (c != 0)
+                {
+                    while (i < len)
+                    {
+                        z[i] = x[i];
+                        ++i;
+                    }
+                    return 0;
+                }
+            }
+            return 1;
+        }
+
+        public static uint IncAt(int len, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= len);
+            for (int i = zPos; i < len; ++i)
+            {
+                if (++z[i] != uint.MinValue)
+                {
+                    return 0;
+                }
+            }
+            return 1;
+        }
+
+        public static uint IncAt(int len, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= len);
+            for (int i = zPos; i < len; ++i)
+            {
+                if (++z[zOff + i] != uint.MinValue)
+                {
+                    return 0;
+                }
+            }
+            return 1;
+        }
+
+        public static bool IsOne(int len, uint[] x)
+        {
+            if (x[0] != 1)
+            {
+                return false;
+            }
+            for (int i = 1; i < len; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static bool IsZero(int len, uint[] x)
+        {
+            if (x[0] != 0)
+            {
+                return false;
+            }
+            for (int i = 1; i < len; ++i)
+            {
+                if (x[i] != 0)
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public static void Mul(int len, uint[] x, uint[] y, uint[] zz)
+        {
+            zz[len] = MulWord(len, x[0], y, zz);
+
+            for (int i = 1; i < len; ++i)
+            {
+                zz[i + len] = MulWordAddTo(len, x[i], y, 0, zz, i);
+            }
+        }
+
+        public static void Mul(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            zz[zzOff + len] = MulWord(len, x[xOff], y, yOff, zz, zzOff);
+
+            for (int i = 1; i < len; ++i)
+            {
+                zz[zzOff + i + len] = MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff + i);
+            }
+        }
+
+        public static void Mul(uint[] x, int xOff, int xLen, uint[] y, int yOff, int yLen, uint[] zz, int zzOff)
+        {
+            zz[zzOff + yLen] = MulWord(yLen, x[xOff], y, yOff, zz, zzOff);
+
+            for (int i = 1; i < xLen; ++i)
+            {
+                zz[zzOff + i + yLen] = MulWordAddTo(yLen, x[xOff + i], y, yOff, zz, zzOff + i);
+            }
+        }
+
+        public static uint MulAddTo(int len, uint[] x, uint[] y, uint[] zz)
+        {
+            ulong zc = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ulong c = MulWordAddTo(len, x[i], y, 0, zz, i) & M;
+                c += zc + (zz[i + len] & M);
+                zz[i + len] = (uint)c;
+                zc = c >> 32;
+            }
+            return (uint)zc;
+        }
+
+        public static uint MulAddTo(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] zz, int zzOff)
+        {
+            ulong zc = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                ulong c = MulWordAddTo(len, x[xOff + i], y, yOff, zz, zzOff) & M;
+                c += zc + (zz[zzOff + len] & M);
+                zz[zzOff + len] = (uint)c;
+                zc = c >> 32;
+                ++zzOff;
+            }
+            return (uint)zc;
+        }
+
+        public static uint Mul31BothAdd(int len, uint a, uint[] x, uint b, uint[] y, uint[] z, int zOff)
+        {
+            ulong c = 0, aVal = (ulong)a, bVal = (ulong)b;
+            int i = 0;
+            do
+            {
+                c += aVal * x[i] + bVal * y[i] + z[zOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < len);
+            return (uint)c;
+        }
+
+        public static uint MulWord(int len, uint x, uint[] y, uint[] z)
+        {
+            ulong c = 0, xVal = (ulong)x;
+            int i = 0;
+            do
+            {
+                c += xVal * y[i];
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < len);
+            return (uint)c;
+        }
+
+        public static uint MulWord(int len, uint x, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = (ulong)x;
+            int i = 0;
+            do
+            {
+                c += xVal * y[yOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < len);
+            return (uint)c;
+        }
+
+        public static uint MulWordAddTo(int len, uint x, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = (ulong)x;
+            int i = 0;
+            do
+            {
+                c += xVal * y[yOff + i] + z[zOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < len);
+            return (uint)c;
+        }
+
+        public static uint MulWordDwordAddAt(int len, uint x, ulong y, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 3));
+            ulong c = 0, xVal = (ulong)x;
+            c += xVal * (uint)y + z[zPos + 0];
+            z[zPos + 0] = (uint)c;
+            c >>= 32;
+            c += xVal * (y >> 32) + z[zPos + 1];
+            z[zPos + 1] = (uint)c;
+            c >>= 32;
+            c += (ulong)z[zPos + 2];
+            z[zPos + 2] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : IncAt(len, z, zPos + 3);
+        }
+
+        public static uint ShiftDownBit(int len, uint[] z, uint c)
+        {
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = z[i];
+                z[i] = (next >> 1) | (c << 31);
+                c = next;
+            }
+            return c << 31;
+        }
+
+        public static uint ShiftDownBit(int len, uint[] z, int zOff, uint c)
+        {
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = z[zOff + i];
+                z[zOff + i] = (next >> 1) | (c << 31);
+                c = next;
+            }
+            return c << 31;
+        }
+
+        public static uint ShiftDownBit(int len, uint[] x, uint c, uint[] z)
+        {
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = x[i];
+                z[i] = (next >> 1) | (c << 31);
+                c = next;
+            }
+            return c << 31;
+        }
+
+        public static uint ShiftDownBit(int len, uint[] x, int xOff, uint c, uint[] z, int zOff)
+        {
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = x[xOff + i];
+                z[zOff + i] = (next >> 1) | (c << 31);
+                c = next;
+            }
+            return c << 31;
+        }
+
+        public static uint ShiftDownBits(int len, uint[] z, int bits, uint c)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = z[i];
+                z[i] = (next >> bits) | (c << -bits);
+                c = next;
+            }
+            return c << -bits;
+        }
+
+        public static uint ShiftDownBits(int len, uint[] z, int zOff, int bits, uint c)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = z[zOff + i];
+                z[zOff + i] = (next >> bits) | (c << -bits);
+                c = next;
+            }
+            return c << -bits;
+        }
+
+        public static uint ShiftDownBits(int len, uint[] x, int bits, uint c, uint[] z)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = x[i];
+                z[i] = (next >> bits) | (c << -bits);
+                c = next;
+            }
+            return c << -bits;
+        }
+
+        public static uint ShiftDownBits(int len, uint[] x, int xOff, int bits, uint c, uint[] z, int zOff)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = x[xOff + i];
+                z[zOff + i] = (next >> bits) | (c << -bits);
+                c = next;
+            }
+            return c << -bits;
+        }
+
+        public static uint ShiftDownWord(int len, uint[] z, uint c)
+        {
+            int i = len;
+            while (--i >= 0)
+            {
+                uint next = z[i];
+                z[i] = c;
+                c = next;
+            }
+            return c;
+        }
+
+        public static uint ShiftUpBit(int len, uint[] z, uint c)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = z[i];
+                z[i] = (next << 1) | (c >> 31);
+                c = next;
+            }
+            return c >> 31;
+        }
+
+        public static uint ShiftUpBit(int len, uint[] z, int zOff, uint c)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = z[zOff + i];
+                z[zOff + i] = (next << 1) | (c >> 31);
+                c = next;
+            }
+            return c >> 31;
+        }
+
+        public static uint ShiftUpBit(int len, uint[] x, uint c, uint[] z)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = x[i];
+                z[i] = (next << 1) | (c >> 31);
+                c = next;
+            }
+            return c >> 31;
+        }
+
+        public static uint ShiftUpBit(int len, uint[] x, int xOff, uint c, uint[] z, int zOff)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = x[xOff + i];
+                z[zOff + i] = (next << 1) | (c >> 31);
+                c = next;
+            }
+            return c >> 31;
+        }
+
+        public static ulong ShiftUpBit64(int len, ulong[] x, int xOff, ulong c, ulong[] z, int zOff)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                ulong next = x[xOff + i];
+                z[zOff + i] = (next << 1) | (c >> 63);
+                c = next;
+            }
+            return c >> 63;
+        }
+
+        public static uint ShiftUpBits(int len, uint[] z, int bits, uint c)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = z[i];
+                z[i] = (next << bits) | (c >> -bits);
+                c = next;
+            }
+            return c >> -bits;
+        }
+
+        public static uint ShiftUpBits(int len, uint[] z, int zOff, int bits, uint c)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = z[zOff + i];
+                z[zOff + i] = (next << bits) | (c >> -bits);
+                c = next;
+            }
+            return c >> -bits;
+        }
+
+        public static ulong ShiftUpBits64(int len, ulong[] z, int zOff, int bits, ulong c)
+        {
+            Debug.Assert(bits > 0 && bits < 64);
+            for (int i = 0; i < len; ++i)
+            {
+                ulong next = z[zOff + i];
+                z[zOff + i] = (next << bits) | (c >> -bits);
+                c = next;
+            }
+            return c >> -bits;
+        }
+
+        public static uint ShiftUpBits(int len, uint[] x, int bits, uint c, uint[] z)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = x[i];
+                z[i] = (next << bits) | (c >> -bits);
+                c = next;
+            }
+            return c >> -bits;
+        }
+
+        public static uint ShiftUpBits(int len, uint[] x, int xOff, int bits, uint c, uint[] z, int zOff)
+        {
+            Debug.Assert(bits > 0 && bits < 32);
+            for (int i = 0; i < len; ++i)
+            {
+                uint next = x[xOff + i];
+                z[zOff + i] = (next << bits) | (c >> -bits);
+                c = next;
+            }
+            return c >> -bits;
+        }
+
+        public static ulong ShiftUpBits64(int len, ulong[] x, int xOff, int bits, ulong c, ulong[] z, int zOff)
+        {
+            Debug.Assert(bits > 0 && bits < 64);
+            for (int i = 0; i < len; ++i)
+            {
+                ulong next = x[xOff + i];
+                z[zOff + i] = (next << bits) | (c >> -bits);
+                c = next;
+            }
+            return c >> -bits;
+        }
+
+        public static void Square(int len, uint[] x, uint[] zz)
+        {
+            int extLen = len << 1;
+            uint c = 0;
+            int j = len, k = extLen;
+            do
+            {
+                ulong xVal = (ulong)x[--j];
+                ulong p = xVal * xVal;
+                zz[--k] = (c << 31) | (uint)(p >> 33);
+                zz[--k] = (uint)(p >> 1);
+                c = (uint)p;
+            }
+            while (j > 0);
+
+            for (int i = 1; i < len; ++i)
+            {
+                c = SquareWordAdd(x, i, zz);
+                AddWordAt(extLen, c, zz, i << 1);
+            }
+
+            ShiftUpBit(extLen, zz, x[0] << 31);
+        }
+
+        public static void Square(int len, uint[] x, int xOff, uint[] zz, int zzOff)
+        {
+            int extLen = len << 1;
+            uint c = 0;
+            int j = len, k = extLen;
+            do
+            {
+                ulong xVal = (ulong)x[xOff + --j];
+                ulong p = xVal * xVal;
+                zz[zzOff + --k] = (c << 31) | (uint)(p >> 33);
+                zz[zzOff + --k] = (uint)(p >> 1);
+                c = (uint)p;
+            }
+            while (j > 0);
+
+            for (int i = 1; i < len; ++i)
+            {
+                c = SquareWordAdd(x, xOff, i, zz, zzOff);
+                AddWordAt(extLen, c, zz, zzOff, i << 1);
+            }
+
+            ShiftUpBit(extLen, zz, zzOff, x[xOff] << 31);
+        }
+
+        public static uint SquareWordAdd(uint[] x, int xPos, uint[] z)
+        {
+            ulong c = 0, xVal = (ulong)x[xPos];
+            int i = 0;
+            do
+            {
+                c += xVal * x[i] + z[xPos + i];
+                z[xPos + i] = (uint)c;
+                c >>= 32;
+            }
+            while (++i < xPos);
+            return (uint)c;
+        }
+
+        public static uint SquareWordAdd(uint[] x, int xOff, int xPos, uint[] z, int zOff)
+        {
+            ulong c = 0, xVal = (ulong)x[xOff + xPos];
+            int i = 0;
+            do
+            {
+                c += xVal * (x[xOff + i] & M) + (z[xPos + zOff] & M);
+                z[xPos + zOff] = (uint)c;
+                c >>= 32;
+                ++zOff;
+            }
+            while (++i < xPos);
+            return (uint)c;
+        }
+
+        public static int Sub(int len, uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (long)x[i] - y[i];
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            return (int)c;
+        }
+
+        public static int Sub(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (long)x[xOff + i] - y[yOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            return (int)c;
+        }
+        public static int Sub33At(int len, uint x, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            long c = (long)z[zPos + 0] - x;
+            z[zPos + 0] = (uint)c;
+            c >>= 32;
+            c += (long)z[zPos + 1] - 1;
+            z[zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zPos + 2);
+        }
+
+        public static int Sub33At(int len, uint x, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            long c = (long)z[zOff + zPos] - x;
+            z[zOff + zPos] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + zPos + 1] - 1;
+            z[zOff + zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zOff, zPos + 2);
+        }
+
+        public static int Sub33From(int len, uint x, uint[] z)
+        {
+            long c = (long)z[0] - x;
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - 1;
+            z[1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, 2);
+        }
+
+        public static int Sub33From(int len, uint x, uint[] z, int zOff)
+        {
+            long c = (long)z[zOff + 0] - x;
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 1] - 1;
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zOff, 2);
+        }
+
+        public static int SubBothFrom(int len, uint[] x, uint[] y, uint[] z)
+        {
+            long c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (long)z[i] - x[i] - y[i];
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            return (int)c;
+        }
+
+        public static int SubBothFrom(int len, uint[] x, int xOff, uint[] y, int yOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (long)z[zOff + i] - x[xOff + i] - y[yOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            return (int)c;
+        }
+
+        public static int SubDWordAt(int len, ulong x, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            long c = (long)z[zPos + 0] - (long)(x & M);
+            z[zPos + 0] = (uint)c;
+            c >>= 32;
+            c += (long)z[zPos + 1] - (long)(x >> 32);
+            z[zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zPos + 2);
+        }
+
+        public static int SubDWordAt(int len, ulong x, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 2));
+            long c = (long)z[zOff + zPos] - (long)(x & M);
+            z[zOff + zPos] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + zPos + 1] - (long)(x >> 32);
+            z[zOff + zPos + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z,  zOff, zPos + 2);
+        }
+
+        public static int SubDWordFrom(int len, ulong x, uint[] z)
+        {
+            long c = (long)z[0] - (long)(x & M);
+            z[0] = (uint)c;
+            c >>= 32;
+            c += (long)z[1] - (long)(x >> 32);
+            z[1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, 2);
+        }
+
+        public static int SubDWordFrom(int len, ulong x, uint[] z, int zOff)
+        {
+            long c = (long)z[zOff + 0] - (long)(x & M);
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            c += (long)z[zOff + 1] - (long)(x >> 32);
+            z[zOff + 1] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zOff, 2);
+        }
+
+        public static int SubFrom(int len, uint[] x, uint[] z)
+        {
+            long c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (long)z[i] - x[i];
+                z[i] = (uint)c;
+                c >>= 32;
+            }
+            return (int)c;
+        }
+
+        public static int SubFrom(int len, uint[] x, int xOff, uint[] z, int zOff)
+        {
+            long c = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                c += (long)z[zOff + i] - x[xOff + i];
+                z[zOff + i] = (uint)c;
+                c >>= 32;
+            }
+            return (int)c;
+        }
+
+        public static int SubWordAt(int len, uint x, uint[] z, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 1));
+            long c = (long)z[zPos] - x;
+            z[zPos] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zPos + 1);
+        }
+
+        public static int SubWordAt(int len, uint x, uint[] z, int zOff, int zPos)
+        {
+            Debug.Assert(zPos <= (len - 1));
+            long c = (long)z[zOff + zPos] - x;
+            z[zOff + zPos] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zOff, zPos + 1);
+        }
+
+        public static int SubWordFrom(int len, uint x, uint[] z)
+        {
+            long c = (long)z[0] - x;
+            z[0] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, 1);
+        }
+
+        public static int SubWordFrom(int len, uint x, uint[] z, int zOff)
+        {
+            long c = (long)z[zOff + 0] - x;
+            z[zOff + 0] = (uint)c;
+            c >>= 32;
+            return c == 0 ? 0 : DecAt(len, z, zOff, 1);
+        }
+
+        public static BigInteger ToBigInteger(int len, uint[] x)
+        {
+            byte[] bs = new byte[len << 2];
+            for (int i = 0; i < len; ++i)
+            {
+                uint x_i = x[i];
+                if (x_i != 0)
+                {
+                    Pack.UInt32_To_BE(x_i, bs, (len - 1 - i) << 2);
+                }
+            }
+            return new BigInteger(1, bs);
+        }
+
+        public static void Zero(int len, uint[] z)
+        {
+            for (int i = 0; i < len; ++i)
+            {
+                z[i] = 0;
+            }
+        }
+    }
+}

+ 92 - 0
src/Renci.SshNet/Security/BouncyCastle/security/DigestUtilities.cs

@@ -0,0 +1,92 @@
+using System;
+using System.Collections;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Digests;
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+using System.Collections.Generic;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Security
+{
+    /// <remarks>
+    ///  Utility class for creating IDigest objects from their names/Oids
+    /// </remarks>
+    internal sealed class DigestUtilities
+    {
+        private enum DigestAlgorithm {
+            SHA_256
+        };
+
+        private DigestUtilities()
+        {
+        }
+
+        private static readonly IDictionary algorithms = new Dictionary<object, object>();
+        private static readonly IDictionary oids = new Dictionary<object, object>();
+
+        static DigestUtilities()
+        {
+            // Signal to obfuscation tools not to change enum constants
+            ((DigestAlgorithm)Enums.GetArbitraryValue(typeof(DigestAlgorithm))).ToString();
+
+            algorithms["SHA256"] = "SHA-256";
+            algorithms["2.16.840.1.101.3.4.2.1"] = "SHA-256";
+        }
+
+        public static ICollection Algorithms
+        {
+            get { return oids.Keys; }
+        }
+
+        public static IDigest GetDigest(
+            string algorithm)
+        {
+            string upper = algorithm.ToUpper();
+            string mechanism = (string) algorithms[upper];
+
+            if (mechanism == null)
+            {
+                mechanism = upper;
+            }
+
+            try
+            {
+                DigestAlgorithm digestAlgorithm = (DigestAlgorithm)Enums.GetEnumValue(
+                    typeof(DigestAlgorithm), mechanism);
+
+                switch (digestAlgorithm)
+                {
+                    case DigestAlgorithm.SHA_256: return new Sha256Digest();
+                }
+            }
+            catch (ArgumentException)
+            {
+            }
+
+            throw new SecurityUtilityException("Digest " + mechanism + " not recognised.");
+        }
+
+        public static byte[] CalculateDigest(string algorithm, byte[] input)
+        {
+            IDigest digest = GetDigest(algorithm);
+            digest.BlockUpdate(input, 0, input.Length);
+            return DoFinal(digest);
+        }
+
+        public static byte[] DoFinal(
+            IDigest digest)
+        {
+            byte[] b = new byte[digest.GetDigestSize()];
+            digest.DoFinal(b, 0);
+            return b;
+        }
+
+        public static byte[] DoFinal(
+            IDigest	digest,
+            byte[]	input)
+        {
+            digest.BlockUpdate(input, 0, input.Length);
+            return DoFinal(digest);
+        }
+    }
+}

+ 210 - 0
src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs

@@ -0,0 +1,210 @@
+using System;
+using System.Threading;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto;
+using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng;
+using Renci.SshNet.Security.Org.BouncyCastle.Utilities;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Security
+{
+    internal class SecureRandom
+        : Random
+    {
+        private static long counter = Times.NanoTime();
+
+       private static long NextCounterValue()
+        {
+            return Interlocked.Increment(ref counter);
+        }
+
+        private static readonly SecureRandom master = new SecureRandom(new CryptoApiRandomGenerator());
+        private static SecureRandom Master
+        {
+            get { return master; }
+        }
+
+        private static DigestRandomGenerator CreatePrng(string digestName, bool autoSeed)
+        {
+            IDigest digest = DigestUtilities.GetDigest(digestName);
+            if (digest == null)
+                return null;
+            DigestRandomGenerator prng = new DigestRandomGenerator(digest);
+            if (autoSeed)
+            {
+                prng.AddSeedMaterial(NextCounterValue());
+                prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize()));
+            }
+            return prng;
+        }
+
+        public static byte[] GetNextBytes(SecureRandom secureRandom, int length)
+        {
+            byte[] result = new byte[length];
+            secureRandom.NextBytes(result);
+            return result;
+        }
+
+        /// <summary>
+        /// Create and auto-seed an instance based on the given algorithm.
+        /// </summary>
+        /// <remarks>Equivalent to GetInstance(algorithm, true)</remarks>
+        /// <param name="algorithm">e.g. "SHA256PRNG"</param>
+        public static SecureRandom GetInstance(string algorithm)
+        {
+            return GetInstance(algorithm, true);
+        }
+
+        /// <summary>
+        /// Create an instance based on the given algorithm, with optional auto-seeding
+        /// </summary>
+        /// <param name="algorithm">e.g. "SHA256PRNG"</param>
+        /// <param name="autoSeed">If true, the instance will be auto-seeded.</param>
+        public static SecureRandom GetInstance(string algorithm, bool autoSeed)
+        {
+            string upper = algorithm.ToUpper();
+            if (upper.EndsWith("PRNG"))
+            {
+                string digestName = upper.Substring(0, upper.Length - "PRNG".Length);
+                DigestRandomGenerator prng = CreatePrng(digestName, autoSeed);
+                if (prng != null)
+                {
+                    return new SecureRandom(prng);
+                }
+            }
+
+            throw new ArgumentException("Unrecognised PRNG algorithm: " + algorithm, "algorithm");
+        }
+
+        protected readonly IRandomGenerator generator;
+
+        public SecureRandom()
+            : this(CreatePrng("SHA256", true))
+        {
+        }
+
+        /// <summary>Use the specified instance of IRandomGenerator as random source.</summary>
+        /// <remarks>
+        /// This constructor performs no seeding of either the <c>IRandomGenerator</c> or the
+        /// constructed <c>SecureRandom</c>. It is the responsibility of the client to provide
+        /// proper seed material as necessary/appropriate for the given <c>IRandomGenerator</c>
+        /// implementation.
+        /// </remarks>
+        /// <param name="generator">The source to generate all random bytes from.</param>
+        public SecureRandom(IRandomGenerator generator)
+            : base(0)
+        {
+            this.generator = generator;
+        }
+
+        public virtual byte[] GenerateSeed(int length)
+        {
+            return GetNextBytes(Master, length);
+        }
+
+        public virtual void SetSeed(byte[] seed)
+        {
+            generator.AddSeedMaterial(seed);
+        }
+
+        public virtual void SetSeed(long seed)
+        {
+            generator.AddSeedMaterial(seed);
+        }
+
+        public override int Next()
+        {
+            return NextInt() & int.MaxValue;
+        }
+
+        public override int Next(int maxValue)
+        {
+
+            if (maxValue < 2)
+            {
+                if (maxValue < 0)
+                    throw new ArgumentOutOfRangeException("maxValue", "cannot be negative");
+
+                return 0;
+            }
+
+            int bits;
+
+            // Test whether maxValue is a power of 2
+            if ((maxValue & (maxValue - 1)) == 0)
+            {
+                bits = NextInt() & int.MaxValue;
+                return (int)(((long)bits * maxValue) >> 31);
+            }
+
+            int result;
+            do
+            {
+                bits = NextInt() & int.MaxValue;
+                result = bits % maxValue;
+            }
+            while (bits - result + (maxValue - 1) < 0); // Ignore results near overflow
+
+            return result;
+        }
+
+        public override int Next(int minValue, int maxValue)
+        {
+            if (maxValue <= minValue)
+            {
+                if (maxValue == minValue)
+                    return minValue;
+
+                throw new ArgumentException("maxValue cannot be less than minValue");
+            }
+
+            int diff = maxValue - minValue;
+            if (diff > 0)
+                return minValue + Next(diff);
+
+            for (;;)
+            {
+                int i = NextInt();
+
+                if (i >= minValue && i < maxValue)
+                    return i;
+            }
+        }
+
+        public override void NextBytes(byte[] buf)
+        {
+            generator.NextBytes(buf);
+        }
+
+        public virtual void NextBytes(byte[] buf, int off, int len)
+        {
+            generator.NextBytes(buf, off, len);
+        }
+
+        private static readonly double DoubleScale = System.Math.Pow(2.0, 64.0);
+
+        public override double NextDouble()
+        {
+            return Convert.ToDouble((ulong) NextLong()) / DoubleScale;
+        }
+
+        public virtual int NextInt()
+        {
+            byte[] bytes = new byte[4];
+            NextBytes(bytes);
+
+            uint result = bytes[0];
+            result <<= 8;
+            result |= bytes[1];
+            result <<= 8;
+            result |= bytes[2];
+            result <<= 8;
+            result |= bytes[3];
+            return (int)result;
+        }
+
+        public virtual long NextLong()
+        {
+            return ((long)(uint) NextInt() << 32) | (long)(uint) NextInt();
+        }
+    }
+}

+ 36 - 0
src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs

@@ -0,0 +1,36 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Security
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE)
+    [Serializable]
+#endif
+    internal class SecurityUtilityException
+		: Exception
+    {
+        /**
+        * base constructor.
+        */
+        public SecurityUtilityException()
+        {
+        }
+
+		/**
+         * create a SecurityUtilityException with the given message.
+         *
+         * @param message the message to be carried with the exception.
+         */
+        public SecurityUtilityException(
+            string message)
+			: base(message)
+        {
+        }
+
+		public SecurityUtilityException(
+            string		message,
+            Exception	exception)
+			: base(message, exception)
+        {
+        }
+    }
+}

+ 725 - 0
src/Renci.SshNet/Security/BouncyCastle/util/Arrays.cs

@@ -0,0 +1,725 @@
+using System;
+using System.Text;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities
+{
+    /// <summary> General array utilities.</summary>
+    internal abstract class Arrays
+    {
+        public static readonly byte[] EmptyBytes = new byte[0];
+        public static readonly int[] EmptyInts = new int[0];
+
+        public static bool AreAllZeroes(byte[] buf, int off, int len)
+        {
+            uint bits = 0;
+            for (int i = 0; i < len; ++i)
+            {
+                bits |= buf[off + i];
+            }
+            return bits == 0;
+        }
+
+        public static bool AreEqual(
+            bool[]  a,
+            bool[]  b)
+        {
+            if (a == b)
+                return true;
+
+            if (a == null || b == null)
+                return false;
+
+            return HaveSameContents(a, b);
+        }
+
+        public static bool AreEqual(
+            char[] a,
+            char[] b)
+        {
+            if (a == b)
+                return true;
+
+            if (a == null || b == null)
+                return false;
+
+            return HaveSameContents(a, b);
+        }
+
+        /// <summary>
+        /// Are two arrays equal.
+        /// </summary>
+        /// <param name="a">Left side.</param>
+        /// <param name="b">Right side.</param>
+        /// <returns>True if equal.</returns>
+        public static bool AreEqual(
+            byte[]	a,
+            byte[]	b)
+        {
+            if (a == b)
+                return true;
+
+            if (a == null || b == null)
+                return false;
+
+            return HaveSameContents(a, b);
+        }
+
+        [Obsolete("Use 'AreEqual' method instead")]
+        public static bool AreSame(
+            byte[]	a,
+            byte[]	b)
+        {
+            return AreEqual(a, b);
+        }
+
+        /// <summary>
+        /// A constant time equals comparison - does not terminate early if
+        /// test will fail.
+        /// </summary>
+        /// <param name="a">first array</param>
+        /// <param name="b">second array</param>
+        /// <returns>true if arrays equal, false otherwise.</returns>
+        public static bool ConstantTimeAreEqual(
+            byte[]	a,
+            byte[]	b)
+        {
+            int i = a.Length;
+            if (i != b.Length)
+                return false;
+            int cmp = 0;
+            while (i != 0)
+            {
+                --i;
+                cmp |= (a[i] ^ b[i]);
+            }
+            return cmp == 0;
+        }
+
+        public static bool AreEqual(
+            int[]	a,
+            int[]	b)
+        {
+            if (a == b)
+                return true;
+
+            if (a == null || b == null)
+                return false;
+
+            return HaveSameContents(a, b);
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static bool AreEqual(uint[] a, uint[] b)
+        {
+            if (a == b)
+                return true;
+
+            if (a == null || b == null)
+                return false;
+
+            return HaveSameContents(a, b);
+        }
+
+        private static bool HaveSameContents(
+            bool[] a,
+            bool[] b)
+        {
+            int i = a.Length;
+            if (i != b.Length)
+                return false;
+            while (i != 0)
+            {
+                --i;
+                if (a[i] != b[i])
+                    return false;
+            }
+            return true;
+        }
+
+        private static bool HaveSameContents(
+            char[] a,
+            char[] b)
+        {
+            int i = a.Length;
+            if (i != b.Length)
+                return false;
+            while (i != 0)
+            {
+                --i;
+                if (a[i] != b[i])
+                    return false;
+            }
+            return true;
+        }
+
+        private static bool HaveSameContents(
+            byte[]	a,
+            byte[]	b)
+        {
+            int i = a.Length;
+            if (i != b.Length)
+                return false;
+            while (i != 0)
+            {
+                --i;
+                if (a[i] != b[i])
+                    return false;
+            }
+            return true;
+        }
+
+        private static bool HaveSameContents(
+            int[]	a,
+            int[]	b)
+        {
+            int i = a.Length;
+            if (i != b.Length)
+                return false;
+            while (i != 0)
+            {
+                --i;
+                if (a[i] != b[i])
+                    return false;
+            }
+            return true;
+        }
+
+        private static bool HaveSameContents(uint[] a, uint[] b)
+        {
+            int i = a.Length;
+            if (i != b.Length)
+                return false;
+            while (i != 0)
+            {
+                --i;
+                if (a[i] != b[i])
+                    return false;
+            }
+            return true;
+        }
+
+        public static string ToString(
+            object[] a)
+        {
+            StringBuilder sb = new StringBuilder('[');
+            if (a.Length > 0)
+            {
+                sb.Append(a[0]);
+                for (int index = 1; index < a.Length; ++index)
+                {
+                    sb.Append(", ").Append(a[index]);
+                }
+            }
+            sb.Append(']');
+            return sb.ToString();
+        }
+
+        public static int GetHashCode(byte[] data)
+        {
+            if (data == null)
+            {
+                return 0;
+            }
+
+            int i = data.Length;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                hc *= 257;
+                hc ^= data[i];
+            }
+
+            return hc;
+        }
+
+        public static int GetHashCode(byte[] data, int off, int len)
+        {
+            if (data == null)
+            {
+                return 0;
+            }
+
+            int i = len;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                hc *= 257;
+                hc ^= data[off + i];
+            }
+
+            return hc;
+        }
+
+        public static int GetHashCode(int[] data)
+        {
+            if (data == null)
+                return 0;
+
+            int i = data.Length;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                hc *= 257;
+                hc ^= data[i];
+            }
+
+            return hc;
+        }
+
+        public static int GetHashCode(int[] data, int off, int len)
+        {
+            if (data == null)
+                return 0;
+
+            int i = len;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                hc *= 257;
+                hc ^= data[off + i];
+            }
+
+            return hc;
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static int GetHashCode(uint[] data)
+        {
+            if (data == null)
+                return 0;
+
+            int i = data.Length;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                hc *= 257;
+                hc ^= (int)data[i];
+            }
+
+            return hc;
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static int GetHashCode(uint[] data, int off, int len)
+        {
+            if (data == null)
+                return 0;
+
+            int i = len;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                hc *= 257;
+                hc ^= (int)data[off + i];
+            }
+
+            return hc;
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static int GetHashCode(ulong[] data)
+        {
+            if (data == null)
+                return 0;
+
+            int i = data.Length;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                ulong di = data[i];
+                hc *= 257;
+                hc ^= (int)di;
+                hc *= 257;
+                hc ^= (int)(di >> 32);
+            }
+
+            return hc;
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static int GetHashCode(ulong[] data, int off, int len)
+        {
+            if (data == null)
+                return 0;
+
+            int i = len;
+            int hc = i + 1;
+
+            while (--i >= 0)
+            {
+                ulong di = data[off + i];
+                hc *= 257;
+                hc ^= (int)di;
+                hc *= 257;
+                hc ^= (int)(di >> 32);
+            }
+
+            return hc;
+        }
+
+        public static byte[] Clone(
+            byte[] data)
+        {
+            return data == null ? null : (byte[])data.Clone();
+        }
+
+        public static byte[] Clone(
+            byte[] data, 
+            byte[] existing)
+        {
+            if (data == null)
+            {
+                return null;
+            }
+            if ((existing == null) || (existing.Length != data.Length))
+            {
+                return Clone(data);
+            }
+            Array.Copy(data, 0, existing, 0, existing.Length);
+            return existing;
+        }
+
+        public static int[] Clone(
+            int[] data)
+        {
+            return data == null ? null : (int[])data.Clone();
+        }
+
+        internal static uint[] Clone(uint[] data)
+        {
+            return data == null ? null : (uint[])data.Clone();
+        }
+
+        public static long[] Clone(long[] data)
+        {
+            return data == null ? null : (long[])data.Clone();
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static ulong[] Clone(
+            ulong[] data)
+        {
+            return data == null ? null : (ulong[]) data.Clone();
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static ulong[] Clone(
+            ulong[] data, 
+            ulong[] existing)
+        {
+            if (data == null)
+            {
+                return null;
+            }
+            if ((existing == null) || (existing.Length != data.Length))
+            {
+                return Clone(data);
+            }
+            Array.Copy(data, 0, existing, 0, existing.Length);
+            return existing;
+        }
+
+        public static bool Contains(byte[] a, byte n)
+        {
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] == n)
+                    return true;
+            }
+            return false;
+        }
+
+        public static bool Contains(short[] a, short n)
+        {
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] == n)
+                    return true;
+            }
+            return false;
+        }
+
+        public static bool Contains(int[] a, int n)
+        {
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] == n)
+                    return true;
+            }
+            return false;
+        }
+
+        public static void Fill(
+            byte[]	buf,
+            byte	b)
+        {
+            int i = buf.Length;
+            while (i > 0)
+            {
+                buf[--i] = b;
+            }
+        }
+
+        public static void Fill(byte[] buf, int from, int to, byte b)
+        {
+            for (int i = from; i < to; ++i)
+            {
+                buf[i] = b;
+            }
+        }
+
+        public static byte[] CopyOf(byte[] data, int newLength)
+        {
+            byte[] tmp = new byte[newLength];
+            Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+            return tmp;
+        }
+
+        public static char[] CopyOf(char[] data, int newLength)
+        {
+            char[] tmp = new char[newLength];
+            Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+            return tmp;
+        }
+
+        public static int[] CopyOf(int[] data, int newLength)
+        {
+            int[] tmp = new int[newLength];
+            Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+            return tmp;
+        }
+
+        public static long[] CopyOf(long[] data, int newLength)
+        {
+            long[] tmp = new long[newLength];
+            Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+            return tmp;
+        }
+
+        public static BigInteger[] CopyOf(BigInteger[] data, int newLength)
+        {
+            BigInteger[] tmp = new BigInteger[newLength];
+            Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+            return tmp;
+        }
+
+        /**
+         * Make a copy of a range of bytes from the passed in data array. The range can
+         * extend beyond the end of the input array, in which case the return array will
+         * be padded with zeroes.
+         *
+         * @param data the array from which the data is to be copied.
+         * @param from the start index at which the copying should take place.
+         * @param to the final index of the range (exclusive).
+         *
+         * @return a new byte array containing the range given.
+         */
+        public static byte[] CopyOfRange(byte[] data, int from, int to)
+        {
+            int newLength = GetLength(from, to);
+            byte[] tmp = new byte[newLength];
+            Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+            return tmp;
+        }
+
+        public static int[] CopyOfRange(int[] data, int from, int to)
+        {
+            int newLength = GetLength(from, to);
+            int[] tmp = new int[newLength];
+            Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+            return tmp;
+        }
+
+        public static long[] CopyOfRange(long[] data, int from, int to)
+        {
+            int newLength = GetLength(from, to);
+            long[] tmp = new long[newLength];
+            Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+            return tmp;
+        }
+
+        public static BigInteger[] CopyOfRange(BigInteger[] data, int from, int to)
+        {
+            int newLength = GetLength(from, to);
+            BigInteger[] tmp = new BigInteger[newLength];
+            Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+            return tmp;
+        }
+
+        private static int GetLength(int from, int to)
+        {
+            int newLength = to - from;
+            if (newLength < 0)
+                throw new ArgumentException(from + " > " + to);
+            return newLength;
+        }
+
+        public static byte[] Append(byte[] a, byte b)
+        {
+            if (a == null)
+                return new byte[] { b };
+
+            int length = a.Length;
+            byte[] result = new byte[length + 1];
+            Array.Copy(a, 0, result, 0, length);
+            result[length] = b;
+            return result;
+        }
+
+        public static short[] Append(short[] a, short b)
+        {
+            if (a == null)
+                return new short[] { b };
+
+            int length = a.Length;
+            short[] result = new short[length + 1];
+            Array.Copy(a, 0, result, 0, length);
+            result[length] = b;
+            return result;
+        }
+
+        public static int[] Append(int[] a, int b)
+        {
+            if (a == null)
+                return new int[] { b };
+
+            int length = a.Length;
+            int[] result = new int[length + 1];
+            Array.Copy(a, 0, result, 0, length);
+            result[length] = b;
+            return result;
+        }
+
+        public static byte[] Concatenate(byte[] a, byte[] b)
+        {
+            if (a == null)
+                return Clone(b);
+            if (b == null)
+                return Clone(a);
+
+            byte[] rv = new byte[a.Length + b.Length];
+            Array.Copy(a, 0, rv, 0, a.Length);
+            Array.Copy(b, 0, rv, a.Length, b.Length);
+            return rv;
+        }
+
+        public static byte[] ConcatenateAll(params byte[][] vs)
+        {
+            byte[][] nonNull = new byte[vs.Length][];
+            int count = 0;
+            int totalLength = 0;
+
+            for (int i = 0; i < vs.Length; ++i)
+            {
+                byte[] v = vs[i];
+                if (v != null)
+                {
+                    nonNull[count++] = v;
+                    totalLength += v.Length;
+                }
+            }
+
+            byte[] result = new byte[totalLength];
+            int pos = 0;
+
+            for (int j = 0; j < count; ++j)
+            {
+                byte[] v = nonNull[j];
+                Array.Copy(v, 0, result, pos, v.Length);
+                pos += v.Length;
+            }
+
+            return result;
+        }
+
+        public static int[] Concatenate(int[] a, int[] b)
+        {
+            if (a == null)
+                return Clone(b);
+            if (b == null)
+                return Clone(a);
+
+            int[] rv = new int[a.Length + b.Length];
+            Array.Copy(a, 0, rv, 0, a.Length);
+            Array.Copy(b, 0, rv, a.Length, b.Length);
+            return rv;
+        }
+
+        public static byte[] Prepend(byte[] a, byte b)
+        {
+            if (a == null)
+                return new byte[] { b };
+
+            int length = a.Length;
+            byte[] result = new byte[length + 1];
+            Array.Copy(a, 0, result, 1, length);
+            result[0] = b;
+            return result;
+        }
+
+        public static short[] Prepend(short[] a, short b)
+        {
+            if (a == null)
+                return new short[] { b };
+
+            int length = a.Length;
+            short[] result = new short[length + 1];
+            Array.Copy(a, 0, result, 1, length);
+            result[0] = b;
+            return result;
+        }
+
+        public static int[] Prepend(int[] a, int b)
+        {
+            if (a == null)
+                return new int[] { b };
+
+            int length = a.Length;
+            int[] result = new int[length + 1];
+            Array.Copy(a, 0, result, 1, length);
+            result[0] = b;
+            return result;
+        }
+
+        public static byte[] Reverse(byte[] a)
+        {
+            if (a == null)
+                return null;
+
+            int p1 = 0, p2 = a.Length;
+            byte[] result = new byte[p2];
+
+            while (--p2 >= 0)
+            {
+                result[p2] = a[p1++];
+            }
+
+            return result;
+        }
+
+        public static int[] Reverse(int[] a)
+        {
+            if (a == null)
+                return null;
+
+            int p1 = 0, p2 = a.Length;
+            int[] result = new int[p2];
+
+            while (--p2 >= 0)
+            {
+                result[p2] = a[p1++];
+            }
+
+            return result;
+        }
+    }
+}

+ 95 - 0
src/Renci.SshNet/Security/BouncyCastle/util/BigIntegers.cs

@@ -0,0 +1,95 @@
+using System;
+
+using Renci.SshNet.Security.Org.BouncyCastle.Math;
+using Renci.SshNet.Security.Org.BouncyCastle.Security;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities
+{
+    /**
+     * BigInteger utilities.
+     */
+    internal abstract class BigIntegers
+    {
+        private const int MaxIterations = 1000;
+
+        /**
+        * Return the passed in value as an unsigned byte array.
+        *
+        * @param value value to be converted.
+        * @return a byte array without a leading zero byte if present in the signed encoding.
+        */
+        public static byte[] AsUnsignedByteArray(
+            BigInteger n)
+        {
+            return n.ToByteArrayUnsigned();
+        }
+
+        /**
+         * Return the passed in value as an unsigned byte array of specified length, zero-extended as necessary.
+         *
+         * @param length desired length of result array.
+         * @param n value to be converted.
+         * @return a byte array of specified length, with leading zeroes as necessary given the size of n.
+         */
+        public static byte[] AsUnsignedByteArray(int length, BigInteger n)
+        {
+            byte[] bytes = n.ToByteArrayUnsigned();
+
+            if (bytes.Length > length)
+                throw new ArgumentException("standard length exceeded", "n");
+
+            if (bytes.Length == length)
+                return bytes;
+
+            byte[] tmp = new byte[length];
+            Array.Copy(bytes, 0, tmp, tmp.Length - bytes.Length, bytes.Length);
+            return tmp;
+        }
+
+        /**
+        * Return a random BigInteger not less than 'min' and not greater than 'max'
+        * 
+        * @param min the least value that may be generated
+        * @param max the greatest value that may be generated
+        * @param random the source of randomness
+        * @return a random BigInteger value in the range [min,max]
+        */
+        public static BigInteger CreateRandomInRange(
+            BigInteger		min,
+            BigInteger		max,
+            // TODO Should have been just Random class
+            SecureRandom	random)
+        {
+            int cmp = min.CompareTo(max);
+            if (cmp >= 0)
+            {
+                if (cmp > 0)
+                    throw new ArgumentException("'min' may not be greater than 'max'");
+
+                return min;
+            }
+
+            if (min.BitLength > max.BitLength / 2)
+            {
+                return CreateRandomInRange(BigInteger.Zero, max.Subtract(min), random).Add(min);
+            }
+
+            for (int i = 0; i < MaxIterations; ++i)
+            {
+                BigInteger x = new BigInteger(max.BitLength, random);
+                if (x.CompareTo(min) >= 0 && x.CompareTo(max) <= 0)
+                {
+                    return x;
+                }
+            }
+
+            // fall back to a faster (restricted) method
+            return new BigInteger(max.Subtract(min).BitLength - 1, random).Add(min);
+        }
+
+        public static int GetUnsignedByteLength(BigInteger n)
+        {
+            return (n.BitLength + 7) / 8;
+        }
+    }
+}

+ 32 - 0
src/Renci.SshNet/Security/BouncyCastle/util/Enums.cs

@@ -0,0 +1,32 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities
+{
+    internal abstract class Enums
+    {
+        internal static Enum GetEnumValue(System.Type enumType, string s)
+        {
+            // We only want to parse single named constants
+            if (s.Length > 0 && char.IsLetter(s[0]) && s.IndexOf(',') < 0)
+            {
+                s = s.Replace('-', '_');
+                s = s.Replace('/', '_');
+                return (Enum)Enum.Parse(enumType, s, false);
+            }
+
+            throw new ArgumentException();
+        }
+
+        internal static Array GetEnumValues(System.Type enumType)
+        {
+            return Enum.GetValues(enumType);
+        }
+
+        internal static Enum GetArbitraryValue(System.Type enumType)
+        {
+            Array values = GetEnumValues(enumType);
+            int pos = (int)(int.MaxValue) % values.Length;
+            return (Enum)values.GetValue(pos);
+        }
+    }
+}

+ 29 - 0
src/Renci.SshNet/Security/BouncyCastle/util/IMemoable.cs

@@ -0,0 +1,29 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities
+{
+	internal interface IMemoable
+	{
+		/// <summary>
+		/// Produce a copy of this object with its configuration and in its current state.
+		/// </summary>
+		/// <remarks>
+		/// The returned object may be used simply to store the state, or may be used as a similar object
+		/// starting from the copied state.
+		/// </remarks>
+		IMemoable Copy();
+
+		/// <summary>
+		/// Restore a copied object state into this object.
+		/// </summary>
+		/// <remarks>
+		/// Implementations of this method <em>should</em> try to avoid or minimise memory allocation to perform the reset.
+		/// </remarks>
+		/// <param name="other">an object originally {@link #copy() copied} from an object of the same type as this instance.</param>
+		/// <exception cref="InvalidCastException">if the provided object is not of the correct type.</exception>
+		/// <exception cref="MemoableResetException">if the <b>other</b> parameter is in some other way invalid.</exception>
+		void Reset(IMemoable other);
+	}
+
+}
+

+ 29 - 0
src/Renci.SshNet/Security/BouncyCastle/util/Integers.cs

@@ -0,0 +1,29 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities
+{
+    internal abstract class Integers
+    {
+        public static int RotateLeft(int i, int distance)
+        {
+            return (i << distance) ^ (int)((uint)i >> -distance);
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static uint RotateLeft(uint i, int distance)
+        {
+            return (i << distance) ^ (i >> -distance);
+        }
+
+        public static int RotateRight(int i, int distance)
+        {
+            return (int)((uint)i >> distance) ^ (i << -distance);
+        }
+
+        [CLSCompliantAttribute(false)]
+        public static uint RotateRight(uint i, int distance)
+        {
+            return (i >> distance) ^ (i << -distance);
+        }
+    }
+}

+ 27 - 0
src/Renci.SshNet/Security/BouncyCastle/util/MemoableResetException.cs

@@ -0,0 +1,27 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities
+{
+    /**
+     * Exception to be thrown on a failure to reset an object implementing Memoable.
+     * <p>
+     * The exception extends InvalidCastException to enable users to have a single handling case,
+     * only introducing specific handling of this one if required.
+     * </p>
+     */
+    internal class MemoableResetException
+        : InvalidCastException
+    {
+        /**
+         * Basic Constructor.
+         *
+         * @param msg message to be associated with this exception.
+         */
+        public MemoableResetException(string msg)
+            : base(msg)
+        {
+        }
+    }
+
+}
+

+ 14 - 0
src/Renci.SshNet/Security/BouncyCastle/util/Times.cs

@@ -0,0 +1,14 @@
+using System;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities
+{
+    internal sealed class Times
+    {
+        private static long NanosecondsPerTick = 100L;
+
+        public static long NanoTime()
+        {
+            return DateTime.UtcNow.Ticks * NanosecondsPerTick;
+        }
+    }
+}

+ 129 - 0
src/Renci.SshNet/Security/BouncyCastle/util/encoders/Hex.cs

@@ -0,0 +1,129 @@
+using System.IO;
+using System.Text;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities.Encoders
+{
+    /// <summary>
+    /// Class to decode and encode Hex.
+    /// </summary>
+    internal sealed class Hex
+    {
+        private static readonly HexEncoder encoder = new HexEncoder();
+
+        private Hex()
+        {
+        }
+
+        public static string ToHexString(
+            byte[] data)
+        {
+            return ToHexString(data, 0, data.Length);
+        }
+
+        public static string ToHexString(
+            byte[]	data,
+            int		off,
+            int		length)
+        {
+            byte[] hex = Encode(data, off, length);
+            return Encoding.UTF8.GetString(hex, 0, hex.Length);
+        }
+
+        /**
+         * encode the input data producing a Hex encoded byte array.
+         *
+         * @return a byte array containing the Hex encoded data.
+         */
+        public static byte[] Encode(
+            byte[] data)
+        {
+            return Encode(data, 0, data.Length);
+        }
+
+        /**
+         * encode the input data producing a Hex encoded byte array.
+         *
+         * @return a byte array containing the Hex encoded data.
+         */
+        public static byte[] Encode(
+            byte[]	data,
+            int		off,
+            int		length)
+        {
+            MemoryStream bOut = new MemoryStream(length * 2);
+
+            encoder.Encode(data, off, length, bOut);
+
+            return bOut.ToArray();
+        }
+
+        /**
+         * Hex encode the byte data writing it to the given output stream.
+         *
+         * @return the number of bytes produced.
+         */
+        public static int Encode(
+            byte[]	data,
+            Stream	outStream)
+        {
+            return encoder.Encode(data, 0, data.Length, outStream);
+        }
+
+        /**
+         * Hex encode the byte data writing it to the given output stream.
+         *
+         * @return the number of bytes produced.
+         */
+        public static int Encode(
+            byte[]	data,
+            int		off,
+            int		length,
+            Stream	outStream)
+        {
+            return encoder.Encode(data, off, length, outStream);
+        }
+
+        /**
+         * decode the Hex encoded input data. It is assumed the input data is valid.
+         *
+         * @return a byte array representing the decoded data.
+         */
+        public static byte[] Decode(
+            byte[] data)
+        {
+            MemoryStream bOut = new MemoryStream((data.Length + 1) / 2);
+
+            encoder.Decode(data, 0, data.Length, bOut);
+
+            return bOut.ToArray();
+        }
+
+        /**
+         * decode the Hex encoded string data - whitespace will be ignored.
+         *
+         * @return a byte array representing the decoded data.
+         */
+        public static byte[] Decode(
+            string data)
+        {
+            MemoryStream bOut = new MemoryStream((data.Length + 1) / 2);
+
+            encoder.DecodeString(data, bOut);
+
+            return bOut.ToArray();
+        }
+
+        /**
+         * decode the Hex encoded string data writing it to the given output stream,
+         * whitespace characters will be ignored.
+         *
+         * @return the number of bytes produced.
+         */
+        public static int Decode(
+            string	data,
+            Stream	outStream)
+        {
+            return encoder.DecodeString(data, outStream);
+        }
+    }
+}

+ 174 - 0
src/Renci.SshNet/Security/BouncyCastle/util/encoders/HexEncoder.cs

@@ -0,0 +1,174 @@
+using System.IO;
+
+namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities.Encoders
+{
+    internal class HexEncoder
+    {
+        protected readonly byte[] encodingTable =
+        {
+            (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7',
+            (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'
+        };
+
+        /*
+         * set up the decoding table.
+         */
+        protected readonly byte[] decodingTable = new byte[128];
+
+        protected void InitialiseDecodingTable()
+        {
+            Arrays.Fill(decodingTable, (byte)0xff);
+
+            for (int i = 0; i < encodingTable.Length; i++)
+            {
+                decodingTable[encodingTable[i]] = (byte)i;
+            }
+
+            decodingTable['A'] = decodingTable['a'];
+            decodingTable['B'] = decodingTable['b'];
+            decodingTable['C'] = decodingTable['c'];
+            decodingTable['D'] = decodingTable['d'];
+            decodingTable['E'] = decodingTable['e'];
+            decodingTable['F'] = decodingTable['f'];
+        }
+
+        public HexEncoder()
+        {
+            InitialiseDecodingTable();
+        }
+
+        /**
+        * encode the input data producing a Hex output stream.
+        *
+        * @return the number of bytes produced.
+        */
+        public int Encode(
+            byte[]	data,
+            int		off,
+            int		length,
+            Stream	outStream)
+        {
+            for (int i = off; i < (off + length); i++)
+            {
+                int v = data[i];
+
+                outStream.WriteByte(encodingTable[v >> 4]);
+                outStream.WriteByte(encodingTable[v & 0xf]);
+            }
+
+            return length * 2;
+        }
+
+        private static bool Ignore(char c)
+        {
+            return c == '\n' || c =='\r' || c == '\t' || c == ' ';
+        }
+
+        /**
+        * decode the Hex encoded byte data writing it to the given output stream,
+        * whitespace characters will be ignored.
+        *
+        * @return the number of bytes produced.
+        */
+        public int Decode(
+            byte[]	data,
+            int		off,
+            int		length,
+            Stream	outStream)
+        {
+            byte b1, b2;
+            int outLen = 0;
+            int end = off + length;
+
+            while (end > off)
+            {
+                if (!Ignore((char)data[end - 1]))
+                {
+                    break;
+                }
+
+                end--;
+            }
+
+            int i = off;
+            while (i < end)
+            {
+                while (i < end && Ignore((char)data[i]))
+                {
+                    i++;
+                }
+
+                b1 = decodingTable[data[i++]];
+
+                while (i < end && Ignore((char)data[i]))
+                {
+                    i++;
+                }
+
+                b2 = decodingTable[data[i++]];
+
+                if ((b1 | b2) >= 0x80)
+                    throw new IOException("invalid characters encountered in Hex data");
+
+                outStream.WriteByte((byte)((b1 << 4) | b2));
+
+                outLen++;
+            }
+
+            return outLen;
+        }
+
+        /**
+        * decode the Hex encoded string data writing it to the given output stream,
+        * whitespace characters will be ignored.
+        *
+        * @return the number of bytes produced.
+        */
+        public int DecodeString(
+            string	data,
+            Stream	outStream)
+        {
+            byte    b1, b2;
+            int     length = 0;
+
+            int     end = data.Length;
+
+            while (end > 0)
+            {
+                if (!Ignore(data[end - 1]))
+                {
+                    break;
+                }
+
+                end--;
+            }
+
+            int i = 0;
+            while (i < end)
+            {
+                while (i < end && Ignore(data[i]))
+                {
+                    i++;
+                }
+
+                b1 = decodingTable[data[i++]];
+
+                while (i < end && Ignore(data[i]))
+                {
+                    i++;
+                }
+
+                b2 = decodingTable[data[i++]];
+
+                if ((b1 | b2) >= 0x80)
+                    throw new IOException("invalid characters encountered in Hex data");
+
+                outStream.WriteByte((byte)((b1 << 4) | b2));
+
+                length++;
+            }
+
+            return length;
+        }
+    }
+}