From d24fe2006fdfa0caa3965c6897ed369f1752f801 Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Tue, 4 Dec 2018 21:20:51 +0100 Subject: [PATCH 1/8] Import minimalistic BouncyCastle for ECDH With some rewrites to avoid import of unneeded features. --- THIRD-PARTY-NOTICES.TXT | 26 + .../Renci.SshNet.NET35.csproj | 218 +- .../Renci.SshNet.NETCore.csproj | 2 +- .../Renci.SshNet.Silverlight5.csproj | 218 +- .../Renci.SshNet.Tests.csproj | 2 +- .../Renci.SshNet.UAP10.csproj | 218 +- .../Renci.SshNet.WindowsPhone8.csproj | 218 +- src/Renci.SshNet/Renci.SshNet.csproj | 72 + .../BouncyCastle/asn1/sec/SECNamedCurves.cs | 121 + .../Security/BouncyCastle/asn1/x9/X9Curve.cs | 40 + .../BouncyCastle/asn1/x9/X9ECParameters.cs | 93 + .../asn1/x9/X9ECParametersHolder.cs | 25 + .../BouncyCastle/asn1/x9/X9ECPoint.cs | 57 + .../crypto/AsymmetricCipherKeyPair.cs | 33 + .../crypto/AsymmetricKeyParameter.cs | 42 + .../IAsymmetricCipherKeyPairGenerator.cs | 9 + .../Security/BouncyCastle/crypto/IDigest.cs | 61 + .../crypto/KeyGenerationParameters.cs | 56 + .../Security/BouncyCastle/crypto/License.html | 39 + .../crypto/agreement/ECDHCBasicAgreement.cs | 46 + .../crypto/digests/GeneralDigest.cs | 129 + .../crypto/digests/Sha256Digest.cs | 280 ++ .../crypto/generators/ECKeyPairGenerator.cs | 91 + .../crypto/parameters/ECDomainParameters.cs | 91 + .../parameters/ECKeyGenerationParameters.cs | 23 + .../crypto/parameters/ECKeyParameters.cs | 79 + .../parameters/ECPrivateKeyParameters.cs | 61 + .../parameters/ECPublicKeyParameters.cs | 60 + .../crypto/prng/CryptoApiRandomGenerator.cs | 73 + .../crypto/prng/DigestRandomGenerator.cs | 117 + .../crypto/prng/IRandomGenerator.cs | 26 + .../Security/BouncyCastle/crypto/util/Pack.cs | 345 ++ .../Security/BouncyCastle/math/BigInteger.cs | 3601 +++++++++++++++++ .../BouncyCastle/math/ec/ECAlgorithms.cs | 496 +++ .../Security/BouncyCastle/math/ec/ECCurve.cs | 1278 ++++++ .../BouncyCastle/math/ec/ECFieldElement.cs | 972 +++++ .../BouncyCastle/math/ec/ECLookupTable.cs | 10 + .../Security/BouncyCastle/math/ec/ECPoint.cs | 2122 ++++++++++ .../BouncyCastle/math/ec/ECPointMap.cs | 9 + .../BouncyCastle/math/ec/LongArray.cs | 2206 ++++++++++ .../math/ec/abc/SimpleBigDecimal.cs | 241 ++ .../Security/BouncyCastle/math/ec/abc/Tnaf.cs | 845 ++++ .../BouncyCastle/math/ec/abc/ZTauElement.cs | 36 + .../math/ec/endo/ECEndomorphism.cs | 11 + .../math/ec/endo/GlvEndomorphism.cs | 10 + .../ec/multiplier/AbstractECMultiplier.cs | 29 + .../math/ec/multiplier/ECMultiplier.cs | 18 + .../ec/multiplier/FixedPointCombMultiplier.cs | 58 + .../ec/multiplier/FixedPointPreCompInfo.cs | 43 + .../math/ec/multiplier/FixedPointUtilities.cs | 95 + .../math/ec/multiplier/GlvMultiplier.cs | 40 + .../math/ec/multiplier/IPreCompCallback.cs | 9 + .../math/ec/multiplier/PreCompInfo.cs | 11 + .../math/ec/multiplier/ValidityPreCompInfo.cs | 44 + .../math/ec/multiplier/WNafL2RMultiplier.cs | 98 + .../math/ec/multiplier/WNafPreCompInfo.cs | 46 + .../math/ec/multiplier/WNafUtilities.cs | 579 +++ .../math/ec/multiplier/WTauNafMultiplier.cs | 138 + .../math/ec/multiplier/WTauNafPreCompInfo.cs | 24 + .../BouncyCastle/math/field/FiniteFields.cs | 54 + .../BouncyCastle/math/field/GF2Polynomial.cs | 46 + .../field/GenericPolynomialExtensionField.cs | 63 + .../math/field/IExtensionField.cs | 12 + .../BouncyCastle/math/field/IFiniteField.cs | 11 + .../BouncyCastle/math/field/IPolynomial.cs | 13 + .../math/field/IPolynomialExtensionField.cs | 8 + .../BouncyCastle/math/field/PrimeField.cs | 42 + .../Security/BouncyCastle/math/raw/Mod.cs | 185 + .../Security/BouncyCastle/math/raw/Nat.cs | 1153 ++++++ .../BouncyCastle/security/DigestUtilities.cs | 92 + .../BouncyCastle/security/SecureRandom.cs | 210 + .../security/SecurityUtilityException.cs | 36 + .../Security/BouncyCastle/util/Arrays.cs | 725 ++++ .../Security/BouncyCastle/util/BigIntegers.cs | 95 + .../Security/BouncyCastle/util/Enums.cs | 32 + .../Security/BouncyCastle/util/IMemoable.cs | 29 + .../Security/BouncyCastle/util/Integers.cs | 29 + .../util/MemoableResetException.cs | 27 + .../Security/BouncyCastle/util/Times.cs | 14 + .../BouncyCastle/util/encoders/Hex.cs | 129 + .../BouncyCastle/util/encoders/HexEncoder.cs | 174 + 81 files changed, 19213 insertions(+), 6 deletions(-) create mode 100644 THIRD-PARTY-NOTICES.TXT create mode 100644 src/Renci.SshNet/Security/BouncyCastle/asn1/sec/SECNamedCurves.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9Curve.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParameters.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParametersHolder.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECPoint.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricCipherKeyPair.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricKeyParameter.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/IAsymmetricCipherKeyPairGenerator.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/IDigest.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/KeyGenerationParameters.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/License.html create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/agreement/ECDHCBasicAgreement.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/digests/GeneralDigest.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/digests/Sha256Digest.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/generators/ECKeyPairGenerator.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECDomainParameters.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyGenerationParameters.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyParameters.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPrivateKeyParameters.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPublicKeyParameters.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/prng/DigestRandomGenerator.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/crypto/util/Pack.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/ECAlgorithms.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/ECCurve.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/ECFieldElement.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/ECLookupTable.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPoint.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPointMap.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/LongArray.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/SimpleBigDecimal.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/Tnaf.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/ZTauElement.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/ECEndomorphism.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/AbstractECMultiplier.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ECMultiplier.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointCombMultiplier.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointPreCompInfo.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointUtilities.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/GlvMultiplier.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/IPreCompCallback.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/PreCompInfo.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ValidityPreCompInfo.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafL2RMultiplier.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafPreCompInfo.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafUtilities.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafMultiplier.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafPreCompInfo.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/FiniteFields.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/GF2Polynomial.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/GenericPolynomialExtensionField.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/IExtensionField.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/IFiniteField.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomial.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomialExtensionField.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/field/PrimeField.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/raw/Mod.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/math/raw/Nat.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/security/DigestUtilities.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/Arrays.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/BigIntegers.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/Enums.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/IMemoable.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/Integers.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/MemoableResetException.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/Times.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/encoders/Hex.cs create mode 100644 src/Renci.SshNet/Security/BouncyCastle/util/encoders/HexEncoder.cs diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT new file mode 100644 index 000000000..dd1b8a210 --- /dev/null +++ b/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. diff --git a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj index a6878d187..a41e7bcb3 100644 --- a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj +++ b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj @@ -602,6 +602,222 @@ Security\Algorithm.cs + + Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs + + + Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs + + + Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\util\Pack.cs + + + Security\Cryptography\BouncyCastle\math\BigInteger.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\LongArray.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs + + + Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\PrimeField.cs + + + Security\Cryptography\BouncyCastle\math\raw\Mod.cs + + + Security\Cryptography\BouncyCastle\math\raw\Nat.cs + + + Security\Cryptography\BouncyCastle\security\DigestUtilities.cs + + + Security\Cryptography\BouncyCastle\security\SecureRandom.cs + + + Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs + + + Security\Cryptography\BouncyCastle\util\Arrays.cs + + + Security\Cryptography\BouncyCastle\util\BigIntegers.cs + + + Security\Cryptography\BouncyCastle\util\encoders\Hex.cs + + + Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs + + + Security\Cryptography\BouncyCastle\util\Enums.cs + + + Security\Cryptography\BouncyCastle\util\IMemoable.cs + + + Security\Cryptography\BouncyCastle\util\Integers.cs + + + Security\Cryptography\BouncyCastle\util\MemoableResetException.cs + + + Security\Cryptography\BouncyCastle\util\Times.cs + Security\CertificateHostAlgorithm.cs @@ -981,4 +1197,4 @@ --> - \ No newline at end of file + diff --git a/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj b/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj index ba1c5b855..bf001750e 100644 --- a/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj +++ b/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj @@ -34,4 +34,4 @@ 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 - \ No newline at end of file + diff --git a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj b/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj index 910eb7708..e66276007 100644 --- a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj +++ b/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj @@ -602,6 +602,222 @@ Security\Algorithm.cs + + Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs + + + Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs + + + Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\util\Pack.cs + + + Security\Cryptography\BouncyCastle\math\BigInteger.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\LongArray.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs + + + Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\PrimeField.cs + + + Security\Cryptography\BouncyCastle\math\raw\Mod.cs + + + Security\Cryptography\BouncyCastle\math\raw\Nat.cs + + + Security\Cryptography\BouncyCastle\security\DigestUtilities.cs + + + Security\Cryptography\BouncyCastle\security\SecureRandom.cs + + + Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs + + + Security\Cryptography\BouncyCastle\util\Arrays.cs + + + Security\Cryptography\BouncyCastle\util\BigIntegers.cs + + + Security\Cryptography\BouncyCastle\util\encoders\Hex.cs + + + Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs + + + Security\Cryptography\BouncyCastle\util\Enums.cs + + + Security\Cryptography\BouncyCastle\util\IMemoable.cs + + + Security\Cryptography\BouncyCastle\util\Integers.cs + + + Security\Cryptography\BouncyCastle\util\MemoableResetException.cs + + + Security\Cryptography\BouncyCastle\util\Times.cs + Security\CertificateHostAlgorithm.cs @@ -980,4 +1196,4 @@ --> - \ No newline at end of file + diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index d968dd27b..0575dbd82 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -717,4 +717,4 @@ --> - \ No newline at end of file + diff --git a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj b/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj index 87316c9d9..369c3da7d 100644 --- a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj +++ b/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj @@ -666,6 +666,222 @@ Security\Algorithm.cs + + Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs + + + Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs + + + Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\util\Pack.cs + + + Security\Cryptography\BouncyCastle\math\BigInteger.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\LongArray.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs + + + Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\PrimeField.cs + + + Security\Cryptography\BouncyCastle\math\raw\Mod.cs + + + Security\Cryptography\BouncyCastle\math\raw\Nat.cs + + + Security\Cryptography\BouncyCastle\security\DigestUtilities.cs + + + Security\Cryptography\BouncyCastle\security\SecureRandom.cs + + + Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs + + + Security\Cryptography\BouncyCastle\util\Arrays.cs + + + Security\Cryptography\BouncyCastle\util\BigIntegers.cs + + + Security\Cryptography\BouncyCastle\util\encoders\Hex.cs + + + Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs + + + Security\Cryptography\BouncyCastle\util\Enums.cs + + + Security\Cryptography\BouncyCastle\util\IMemoable.cs + + + Security\Cryptography\BouncyCastle\util\Integers.cs + + + Security\Cryptography\BouncyCastle\util\MemoableResetException.cs + + + Security\Cryptography\BouncyCastle\util\Times.cs + Security\CertificateHostAlgorithm.cs @@ -1034,4 +1250,4 @@ --> - \ No newline at end of file + diff --git a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj b/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj index f1bd9c407..9300a9b41 100644 --- a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj +++ b/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj @@ -631,6 +631,222 @@ Security\Algorithm.cs + + Security\Cryptography\BouncyCastle\asn1\sec\SECNamedCurves.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9Curve.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParameters.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECParametersHolder.cs + + + Security\Cryptography\BouncyCastle\asn1\x9\X9ECPoint.cs + + + Security\Cryptography\BouncyCastle\crypto\agreement\ECDHCBasicAgreement.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricCipherKeyPair.cs + + + Security\Cryptography\BouncyCastle\crypto\AsymmetricKeyParameter.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\GeneralDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\digests\Sha256Digest.cs + + + Security\Cryptography\BouncyCastle\crypto\generators\ECKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IAsymmetricCipherKeyPairGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\IDigest.cs + + + Security\Cryptography\BouncyCastle\crypto\KeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECDomainParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyGenerationParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPrivateKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\parameters\ECPublicKeyParameters.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\CryptoApiRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\DigestRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\prng\IRandomGenerator.cs + + + Security\Cryptography\BouncyCastle\crypto\util\Pack.cs + + + Security\Cryptography\BouncyCastle\math\BigInteger.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\SimpleBigDecimal.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\Tnaf.cs + + + Security\Cryptography\BouncyCastle\math\ec\abc\ZTauElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECAlgorithms.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECCurve.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECFieldElement.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECLookupTable.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPoint.cs + + + Security\Cryptography\BouncyCastle\math\ec\ECPointMap.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\ECEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\endo\GlvEndomorphism.cs + + + Security\Cryptography\BouncyCastle\math\ec\LongArray.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\AbstractECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ECMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointCombMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\FixedPointUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\GlvMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\IPreCompCallback.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\PreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\ValidityPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafL2RMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WNafUtilities.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafMultiplier.cs + + + Security\Cryptography\BouncyCastle\math\ec\multiplier\WTauNafPreCompInfo.cs + + + Security\Cryptography\BouncyCastle\math\field\FiniteFields.cs + + + Security\Cryptography\BouncyCastle\math\field\GenericPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\GF2Polynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\IFiniteField.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomial.cs + + + Security\Cryptography\BouncyCastle\math\field\IPolynomialExtensionField.cs + + + Security\Cryptography\BouncyCastle\math\field\PrimeField.cs + + + Security\Cryptography\BouncyCastle\math\raw\Mod.cs + + + Security\Cryptography\BouncyCastle\math\raw\Nat.cs + + + Security\Cryptography\BouncyCastle\security\DigestUtilities.cs + + + Security\Cryptography\BouncyCastle\security\SecureRandom.cs + + + Security\Cryptography\BouncyCastle\security\SecurityUtilityException.cs + + + Security\Cryptography\BouncyCastle\util\Arrays.cs + + + Security\Cryptography\BouncyCastle\util\BigIntegers.cs + + + Security\Cryptography\BouncyCastle\util\encoders\Hex.cs + + + Security\Cryptography\BouncyCastle\util\encoders\HexEncoder.cs + + + Security\Cryptography\BouncyCastle\util\Enums.cs + + + Security\Cryptography\BouncyCastle\util\IMemoable.cs + + + Security\Cryptography\BouncyCastle\util\Integers.cs + + + Security\Cryptography\BouncyCastle\util\MemoableResetException.cs + + + Security\Cryptography\BouncyCastle\util\Times.cs + Security\CertificateHostAlgorithm.cs @@ -1007,4 +1223,4 @@ --> - \ No newline at end of file + diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 0598d18ce..2084f956d 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -168,6 +168,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Renci.SshNet/Security/BouncyCastle/asn1/sec/SECNamedCurves.cs b/src/Renci.SshNet/Security/BouncyCastle/asn1/sec/SECNamedCurves.cs new file mode 100644 index 000000000..dc9462b3a --- /dev/null +++ b/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)); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9Curve.cs b/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9Curve.cs new file mode 100644 index 000000000..1faf777a8 --- /dev/null +++ b/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); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParameters.cs b/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParameters.cs new file mode 100644 index 000000000..f8234b963 --- /dev/null +++ b/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; } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParametersHolder.cs b/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECParametersHolder.cs new file mode 100644 index 000000000..8574cb035 --- /dev/null +++ b/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(); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECPoint.cs b/src/Renci.SshNet/Security/BouncyCastle/asn1/x9/X9ECPoint.cs new file mode 100644 index 000000000..f4db71063 --- /dev/null +++ b/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); + } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricCipherKeyPair.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricCipherKeyPair.cs new file mode 100644 index 000000000..e53a66fe3 --- /dev/null +++ b/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; } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricKeyParameter.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/AsymmetricKeyParameter.cs new file mode 100644 index 000000000..fb66739f3 --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/IAsymmetricCipherKeyPairGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/IAsymmetricCipherKeyPairGenerator.cs new file mode 100644 index 000000000..6c19d1ea0 --- /dev/null +++ b/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(); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/IDigest.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/IDigest.cs new file mode 100644 index 000000000..2ff19e3a3 --- /dev/null +++ b/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(); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/KeyGenerationParameters.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/KeyGenerationParameters.cs new file mode 100644 index 000000000..3be654e49 --- /dev/null +++ b/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; } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/License.html b/src/Renci.SshNet/Security/BouncyCastle/crypto/License.html new file mode 100644 index 000000000..829aa6ba3 --- /dev/null +++ b/src/Renci.SshNet/Security/BouncyCastle/crypto/License.html @@ -0,0 +1,39 @@ + + + + + License + + +

The Bouncy Castle Cryptographic C#® API

+

License:

+The Bouncy Castle License
+Copyright (c) 2000-2018 The Legion of the Bouncy Castle Inc. +(https://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, 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:
+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.
+
+
+ + diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/agreement/ECDHCBasicAgreement.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/agreement/ECDHCBasicAgreement.cs new file mode 100644 index 000000000..6814cb176 --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/digests/GeneralDigest.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/digests/GeneralDigest.cs new file mode 100644 index 000000000..718f34ae8 --- /dev/null +++ b/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); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/digests/Sha256Digest.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/digests/Sha256Digest.cs new file mode 100644 index 000000000..da700fcf2 --- /dev/null +++ b/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); + } + + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/generators/ECKeyPairGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/generators/ECKeyPairGenerator.cs new file mode 100644 index 000000000..446c279cd --- /dev/null +++ b/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); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECDomainParameters.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECDomainParameters.cs new file mode 100644 index 000000000..1d1b037f2 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyGenerationParameters.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyGenerationParameters.cs new file mode 100644 index 000000000..88e33e3d3 --- /dev/null +++ b/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; } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyParameters.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECKeyParameters.cs new file mode 100644 index 000000000..50426c54e --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPrivateKeyParameters.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPrivateKeyParameters.cs new file mode 100644 index 000000000..a11233c8d --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPublicKeyParameters.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/parameters/ECPublicKeyParameters.cs new file mode 100644 index 000000000..6b1f3ec5c --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs new file mode 100644 index 000000000..b0c40cb4c --- /dev/null +++ b/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 diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/DigestRandomGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/DigestRandomGenerator.cs new file mode 100644 index 000000000..b0b69b7bc --- /dev/null +++ b/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); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs new file mode 100644 index 000000000..5bb0c3540 --- /dev/null +++ b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/IRandomGenerator.cs @@ -0,0 +1,26 @@ +using System; + +namespace Renci.SshNet.Security.Org.BouncyCastle.Crypto.Prng +{ + /// Generic interface for objects generating random bytes. + internal interface IRandomGenerator + { + /// Add more seed material to the generator. + /// A byte array to be mixed into the generator's state. + void AddSeedMaterial(byte[] seed); + + /// Add more seed material to the generator. + /// A long value to be mixed into the generator's state. + void AddSeedMaterial(long seed); + + /// Fill byte array with random values. + /// Array to be filled. + void NextBytes(byte[] bytes); + + /// Fill byte array with random values. + /// Array to receive bytes. + /// Index to start filling at. + /// Length of segment to fill. + void NextBytes(byte[] bytes, int start, int len); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/util/Pack.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/util/Pack.cs new file mode 100644 index 000000000..594959eb2 --- /dev/null +++ b/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; + } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs b/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs new file mode 100644 index 000000000..b618501a8 --- /dev/null +++ b/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. + *

From Knuth Vol 2, pg 395.

+ */ + 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 + *
+ * Based algorithm 14.36 of Handbook of Applied Cryptography. + *
+ *
  • m, x, y should have length n
  • + *
  • a should have length (n + 1)
  • + *
  • b = 2^32, R = b^n
  • + *
    + * The result is put in x + *
    + * NOTE: the indices of x, y, m, a different in HAC and in Java + */ + private static void MultiplyMonty(int[] a, int[] x, int[] y, int[] m, 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(); + 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(); + 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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECAlgorithms.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECAlgorithms.cs new file mode 100644 index 000000000..1347a9af2 --- /dev/null +++ b/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 kP. + */ + 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; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECCurve.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECCurve.cs new file mode 100644 index 000000000..46c69184d --- /dev/null +++ b/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 +{ + /// Base class for an elliptic curve. + 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 PreCompInfo for a point on this curve, under a given name. Used by + * ECMultipliers to save the precomputation for this ECPoint for use + * by subsequent multiplication. + * + * @param point + * The ECPoint to store precomputations for. + * @param name + * A String used to index precomputations of different types. + * @param callback + * Called to calculate the PreCompInfo. + */ + 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(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 ECCurve 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 ECMultiplier, 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 + * Fp (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 s0 and + * s1 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 z2 + z = beta(X9.62 + * D.1.6) The other solution is z + 1. + * + * @param beta + * The value to solve the quadratic equation for. + * @return the solution for z2 + z = beta or + * null 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 s0 and + * s1 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 + * y2 + xy = x3 + ax2 + b. + */ + internal class F2mCurve + : AbstractF2mCurve + { + private const int F2M_DEFAULT_COORDS = COORD_LAMBDA_PROJECTIVE; + + /** + * The exponent m of F2m. + */ + private readonly int m; + + /** + * TPB: The integer k where xm + + * xk + 1 represents the reduction polynomial + * f(z).
    + * PPB: The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private readonly int k1; + + /** + * TPB: Always set to 0
    + * PPB: The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + private readonly int k2; + + /** + * TPB: Always set to 0
    + * PPB: The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + 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 m of + * F2m. + * @param k The integer k where xm + + * xk + 1 represents the reduction + * polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + */ + [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 m of + * F2m. + * @param k The integer k where xm + + * xk + 1 represents the reduction + * polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param order The order of the main subgroup of the elliptic curve. + * @param cofactor The cofactor of the elliptic curve, i.e. + * #Ea(F2m) = h * n. + */ + 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 m of + * F2m. + * @param k1 The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k2 The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k3 The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + */ + [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 m of + * F2m. + * @param k1 The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k2 The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k3 The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param a The coefficient a in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param b The coefficient b in the Weierstrass equation + * for non-supersingular elliptic curves over + * F2m. + * @param order The order of the main subgroup of the elliptic curve. + * @param cofactor The cofactor of the elliptic curve, i.e. + * #Ea(F2m) = h * n. + */ + 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); + } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECFieldElement.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECFieldElement.cs new file mode 100644 index 000000000..3ebb3332c --- /dev/null +++ b/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 + * F2m 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 m of F2m. + */ + private int m; + + private int[] ks; + + /** + * The LongArray holding the bits. + */ + internal LongArray x; + + /** + * Constructor for Ppb. + * @param m The exponent m of + * F2m. + * @param k1 The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k2 The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @param k3 The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z). + * @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 m of + * F2m. + * @param k The integer k where xm + + * xk + 1 represents the reduction + * polynomial f(z). + * @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 a and b + * are elements of the same field F2m + * (having the same representation). + * @param a field element. + * @param b field element to be compared. + * @throws ArgumentException if a and b + * are not elements of the same field + * F2m (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 + * F2m, 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 m of the reduction polynomial + * f(z). + */ + public int M + { + get { return this.m; } + } + + /** + * @return Tpb: The integer k where xm + + * xk + 1 represents the reduction polynomial + * f(z).
    + * Ppb: The integer k1 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + public int K1 + { + get { return this.ks[0]; } + } + + /** + * @return Tpb: Always returns 0
    + * Ppb: The integer k2 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + public int K2 + { + get { return this.ks.Length >= 2 ? this.ks[1] : 0; } + } + + /** + * @return Tpb: Always set to 0
    + * Ppb: The integer k3 where xm + + * xk3 + xk2 + xk1 + 1 + * represents the reduction polynomial f(z).
    + */ + 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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECLookupTable.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECLookupTable.cs new file mode 100644 index 000000000..0b9a3999f --- /dev/null +++ b/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); + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPoint.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPoint.cs new file mode 100644 index 000000000..3a93c3e76 --- /dev/null +++ b/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 ECPoint by the given number. + * @param k The multiplicator. + * @return k * this. + */ + 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"); + } + } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPointMap.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/ECPointMap.cs new file mode 100644 index 000000000..9f2819951 --- /dev/null +++ b/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); + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/LongArray.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/LongArray.cs new file mode 100644 index 000000000..6b51b5ff6 --- /dev/null +++ b/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(); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/SimpleBigDecimal.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/SimpleBigDecimal.cs new file mode 100644 index 000000000..23604d1ba --- /dev/null +++ b/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 + * SimpleBigDecimal 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 scale of the SimpleBigDecimal. + * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted + * automatically, but must be set manually. All SimpleBigDecimals + * taking part in the same arithmetic operation must have equal scale. The + * result of a multiplication of two SimpleBigDecimals returns a + * SimpleBigDecimal with double scale. + */ + internal class SimpleBigDecimal + // : Number + { + // private static final long serialVersionUID = 1L; + + private readonly BigInteger bigInt; + private readonly int scale; + + /** + * Returns a SimpleBigDecimal representing the same numerical + * value as value. + * @param value The value of the SimpleBigDecimal to be + * created. + * @param scale The scale of the SimpleBigDecimal to be + * created. + * @return The such created SimpleBigDecimal. + */ + public static SimpleBigDecimal GetInstance(BigInteger val, int scale) + { + return new SimpleBigDecimal(val.ShiftLeft(scale), scale); + } + + /** + * Constructor for SimpleBigDecimal. The value of the + * constructed SimpleBigDecimal Equals bigInt / + * 2scale. + * @param bigInt The bigInt value parameter. + * @param scale The scale of the constructed SimpleBigDecimal. + */ + 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; + } + + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/Tnaf.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/Tnaf.cs new file mode 100644 index 000000000..86e582f2a --- /dev/null +++ b/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 + * τ-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 + * αu'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; + + /** + * 24 + */ + public const sbyte Pow2Width = 16; + + /** + * The αu's for a=0 as an array + * of ZTauElements. + */ + 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 αu's for a=0 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 αu's for a=1 as an array + * of ZTauElements. + */ + 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 αu's for a=1 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 λ of + * Z[τ]. + * @param mu The parameter μ of the elliptic curve. + * @param lambda The element λ of + * Z[τ]. + * @return The norm of λ. + */ + 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 λ of + * R[τ], where λ = u + vτ + * and u and u are real numbers (elements of + * R). + * @param mu The parameter μ of the elliptic curve. + * @param u The real part of the element λ of + * R[τ]. + * @param v The τ-adic part of the element + * λ of R[τ]. + * @return The norm of λ. + */ + 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 λ of R[τ] + * to an element of Z[τ], such that their difference + * has minimal norm. λ is given as + * λ = λ0 + λ1τ. + * @param lambda0 The component λ0. + * @param lambda1 The component λ1. + * @param mu The parameter μ of the elliptic curve. Must + * equal 1 or -1. + * @return The rounded element of Z[τ]. + * @throws ArgumentException if lambda0 and + * lambda1 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 n. For an integer + * k, the value λ = s k / n is + * computed to c bits of accuracy. + * @param k The parameter k. + * @param s The curve parameter s0 or + * s1. + * @param vm The Lucas Sequence element Vm. + * @param a The parameter a of the elliptic curve. + * @param m The bit length of the finite field + * Fm. + * @param c The number of bits of accuracy, i.e. the scale of the returned + * SimpleBigDecimal. + * @return The value λ = s k / n computed to + * c 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 τ-adic NAF (non-adjacent form) of an + * element λ of Z[τ]. + * @param mu The parameter μ of the elliptic curve. + * @param lambda The element λ of + * Z[τ]. + * @return The τ-adic NAF of λ. + */ + 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 τ() to an + * AbstractF2mPoint. + * @param p The AbstractF2mPoint to which τ() is applied. + * @return τ(p) + */ + public static AbstractF2mPoint Tau(AbstractF2mPoint p) + { + return p.Tau(); + } + + /** + * Returns the parameter μ of the elliptic curve. + * @param curve The elliptic curve from which to obtain μ. + * The curve must be a Koblitz curve, i.e. a Equals + * 0 or 1 and b Equals + * 1. + * @return μ 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 Uk-1 and + * Uk or Vk-1 and + * Vk. + * @param mu The parameter μ 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 Vk-1 and + * Vk, otherwise Uk-1 and + * Uk. + * @return An array with 2 elements, containing Uk-1 + * and Uk or Vk-1 + * and Vk. + */ + 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 tw. If the width is + * 4, then for mu = 1, tw = 6 and for + * mu = -1, tw = 10 + * @param mu The parameter μ of the elliptic curve. + * @param w The window width of the WTNAF. + * @return the auxiliary value tw + */ + 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 s0 and + * s1 used for partial modular reduction. + * @param curve The elliptic curve for which to compute + * s0 and s1. + * @throws ArgumentException if curve 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 + * m - 1)/(τ - 1). + * @param k The integer to be reduced. + * @param m The bitlength of the underlying finite field. + * @param a The parameter a of the elliptic curve. + * @param s The auxiliary values s0 and + * s1. + * @param mu The parameter μ of the elliptic curve. + * @param c The precision (number of bits of accuracy) of the partial + * modular reduction. + * @return ρ := k partmod (τm - 1)/(τ - 1) + */ + 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 BigInteger using the reduced τ-adic + * NAF (RTNAF) method. + * @param p The AbstractF2mPoint to Multiply. + * @param k The BigInteger by which to Multiply p. + * @return k * p + */ + 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 λ of Z[τ] + * using the τ-adic NAF (TNAF) method. + * @param p The AbstractF2mPoint to Multiply. + * @param lambda The element λ of + * Z[τ]. + * @return λ * p + */ + 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 λ of Z[τ] + * using the τ-adic NAF (TNAF) method, given the TNAF + * of λ. + * @param p The AbstractF2mPoint to Multiply. + * @param u The the TNAF of λ.. + * @return λ * p + */ + 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 [τ]-adic window NAF of an element + * λ of Z[τ]. + * @param mu The parameter μ of the elliptic curve. + * @param lambda The element λ of + * Z[τ] of which to compute the + * [τ]-adic NAF. + * @param width The window width of the resulting WNAF. + * @param pow2w 2width. + * @param tw The auxiliary value tw. + * @param alpha The αu's for the window width. + * @return The [τ]-adic window NAF of + * λ. + */ + 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 ECPoint for which to do the precomputation. + * @param a The parameter a of the elliptic curve. + * @return The precomputation array for p. + */ + 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; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/ZTauElement.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/abc/ZTauElement.cs new file mode 100644 index 000000000..0b74e48d9 --- /dev/null +++ b/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 Z[τ]. Let + * λ be an element of Z[τ]. Then + * λ is given as λ = u + vτ. The + * components u and v may be used directly, there + * are no accessor methods. + * Immutable class. + */ + internal class ZTauElement + { + /** + * The "real" part of λ. + */ + public readonly BigInteger u; + + /** + * The "τ-adic" part of λ. + */ + public readonly BigInteger v; + + /** + * Constructor for an element λ of + * Z[τ]. + * @param u The "real" part of λ. + * @param v The "τ-adic" part of + * λ. + */ + public ZTauElement(BigInteger u, BigInteger v) + { + this.u = u; + this.v = v; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/ECEndomorphism.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/ECEndomorphism.cs new file mode 100644 index 000000000..8dbbace40 --- /dev/null +++ b/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; } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/endo/GlvEndomorphism.cs new file mode 100644 index 000000000..143a369b3 --- /dev/null +++ b/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); + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/AbstractECMultiplier.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/AbstractECMultiplier.cs new file mode 100644 index 000000000..9c9b4cb27 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ECMultiplier.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ECMultiplier.cs new file mode 100644 index 000000000..97fe1d131 --- /dev/null +++ b/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 ECPoints. + */ + internal interface ECMultiplier + { + /** + * Multiplies the ECPoint p by k, i.e. + * p is added k times to itself. + * @param p The ECPoint to be multiplied. + * @param k The factor by which p is multiplied. + * @return p multiplied by k. + */ + ECPoint Multiply(ECPoint p, BigInteger k); + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointCombMultiplier.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointCombMultiplier.cs new file mode 100644 index 000000000..a63e18e25 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointPreCompInfo.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointPreCompInfo.cs new file mode 100644 index 000000000..d54116575 --- /dev/null +++ b/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 ECPoints 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; } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointUtilities.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/FixedPointUtilities.cs new file mode 100644 index 000000000..14dea10a7 --- /dev/null +++ b/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; + } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/GlvMultiplier.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/GlvMultiplier.cs new file mode 100644 index 000000000..e255b3ee8 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/IPreCompCallback.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/IPreCompCallback.cs new file mode 100644 index 000000000..871998899 --- /dev/null +++ b/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); + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/PreCompInfo.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/PreCompInfo.cs new file mode 100644 index 000000000..32c40216c --- /dev/null +++ b/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 + * WNafMultiplier. + */ + internal interface PreCompInfo + { + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ValidityPreCompInfo.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/ValidityPreCompInfo.cs new file mode 100644 index 000000000..78dabc26d --- /dev/null +++ b/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; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafL2RMultiplier.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafL2RMultiplier.cs new file mode 100644 index 000000000..043415661 --- /dev/null +++ b/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 this by an integer k using the + * Window NAF method. + * @param k The integer by which this is multiplied. + * @return A new ECPoint which equals this + * multiplied by k. + */ + 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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafPreCompInfo.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafPreCompInfo.cs new file mode 100644 index 000000000..91d56512f --- /dev/null +++ b/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 ECPoints used for a Window + * NAF multiplication. + */ + protected ECPoint[] m_preComp = null; + + /** + * Array holding the negations of the precomputed ECPoints used + * for a Window NAF multiplication. + */ + protected ECPoint[] m_preCompNeg = null; + + /** + * Holds an ECPoint 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; } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafUtilities.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WNafUtilities.cs new file mode 100644 index 000000000..8dbe7ef73 --- /dev/null +++ b/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 w of the Window NAF. The width is + * defined as the minimal number w, such that for any + * w 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: + * k = &sum;i=0l-1 ki2i + * , where the ki denote the elements of the + * returned byte[]. + */ + 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; + } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafMultiplier.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafMultiplier.cs new file mode 100644 index 000000000..1d19bdb6e --- /dev/null +++ b/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 + * τ-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 k using the reduced τ-adic NAF (RTNAF) + * method. + * @param p The AbstractF2mPoint to multiply. + * @param k The integer by which to multiply k. + * @return p multiplied by k. + */ + 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 λ of Z[τ] using + * the τ-adic NAF (TNAF) method. + * @param p The AbstractF2mPoint to multiply. + * @param lambda The element λ of + * Z[τ] of which to compute the + * [τ]-adic NAF. + * @return p multiplied by λ. + */ + 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 λ of Z[τ] + * using the window τ-adic NAF (TNAF) method, given the + * WTNAF of λ. + * @param p The AbstractF2mPoint to multiply. + * @param u The the WTNAF of λ.. + * @return λ * p + */ + 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; + } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafPreCompInfo.cs b/src/Renci.SshNet/Security/BouncyCastle/math/ec/multiplier/WTauNafPreCompInfo.cs new file mode 100644 index 000000000..f0a6f489d --- /dev/null +++ b/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 + * τ-adic Non-Adjacent Form) algorithm. + */ + internal class WTauNafPreCompInfo + : PreCompInfo + { + /** + * Array holding the precomputed AbstractF2mPoints used for the + * WTNAF multiplication in + * {@link org.bouncycastle.math.ec.multiplier.WTauNafMultiplier.multiply() + * WTauNafMultiplier.multiply()}. + */ + protected AbstractF2mPoint[] m_preComp; + + public virtual AbstractF2mPoint[] PreComp + { + get { return m_preComp; } + set { this.m_preComp = value; } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/FiniteFields.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/FiniteFields.cs new file mode 100644 index 000000000..77533c761 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/GF2Polynomial.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/GF2Polynomial.cs new file mode 100644 index 000000000..a7c916db6 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/GenericPolynomialExtensionField.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/GenericPolynomialExtensionField.cs new file mode 100644 index 000000000..29b4f57d8 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/IExtensionField.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/IExtensionField.cs new file mode 100644 index 000000000..242bcd00c --- /dev/null +++ b/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; } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/IFiniteField.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/IFiniteField.cs new file mode 100644 index 000000000..fa6b6fca1 --- /dev/null +++ b/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; } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomial.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomial.cs new file mode 100644 index 000000000..06a61663c --- /dev/null +++ b/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(); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomialExtensionField.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/IPolynomialExtensionField.cs new file mode 100644 index 000000000..a419bec83 --- /dev/null +++ b/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; } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/field/PrimeField.cs b/src/Renci.SshNet/Security/BouncyCastle/math/field/PrimeField.cs new file mode 100644 index 000000000..c0651e147 --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/raw/Mod.cs b/src/Renci.SshNet/Security/BouncyCastle/math/raw/Mod.cs new file mode 100644 index 000000000..1fa6b6da1 --- /dev/null +++ b/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; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/raw/Nat.cs b/src/Renci.SshNet/Security/BouncyCastle/math/raw/Nat.cs new file mode 100644 index 000000000..710c2497a --- /dev/null +++ b/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; + } + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/security/DigestUtilities.cs b/src/Renci.SshNet/Security/BouncyCastle/security/DigestUtilities.cs new file mode 100644 index 000000000..a04ef5f4c --- /dev/null +++ b/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 +{ + /// + /// Utility class for creating IDigest objects from their names/Oids + /// + internal sealed class DigestUtilities + { + private enum DigestAlgorithm { + SHA_256 + }; + + private DigestUtilities() + { + } + + private static readonly IDictionary algorithms = new Dictionary(); + private static readonly IDictionary oids = new Dictionary(); + + 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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs b/src/Renci.SshNet/Security/BouncyCastle/security/SecureRandom.cs new file mode 100644 index 000000000..f63412a23 --- /dev/null +++ b/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; + } + + /// + /// Create and auto-seed an instance based on the given algorithm. + /// + /// Equivalent to GetInstance(algorithm, true) + /// e.g. "SHA256PRNG" + public static SecureRandom GetInstance(string algorithm) + { + return GetInstance(algorithm, true); + } + + /// + /// Create an instance based on the given algorithm, with optional auto-seeding + /// + /// e.g. "SHA256PRNG" + /// If true, the instance will be auto-seeded. + 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)) + { + } + + /// Use the specified instance of IRandomGenerator as random source. + /// + /// This constructor performs no seeding of either the IRandomGenerator or the + /// constructed SecureRandom. It is the responsibility of the client to provide + /// proper seed material as necessary/appropriate for the given IRandomGenerator + /// implementation. + /// + /// The source to generate all random bytes from. + 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(); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs b/src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs new file mode 100644 index 000000000..d71893840 --- /dev/null +++ b/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) + { + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/Arrays.cs b/src/Renci.SshNet/Security/BouncyCastle/util/Arrays.cs new file mode 100644 index 000000000..87ffa3071 --- /dev/null +++ b/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 +{ + /// General array utilities. + 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); + } + + /// + /// Are two arrays equal. + /// + /// Left side. + /// Right side. + /// True if equal. + 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); + } + + /// + /// A constant time equals comparison - does not terminate early if + /// test will fail. + /// + /// first array + /// second array + /// true if arrays equal, false otherwise. + 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; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/BigIntegers.cs b/src/Renci.SshNet/Security/BouncyCastle/util/BigIntegers.cs new file mode 100644 index 000000000..6e1b8c855 --- /dev/null +++ b/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; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/Enums.cs b/src/Renci.SshNet/Security/BouncyCastle/util/Enums.cs new file mode 100644 index 000000000..117abef0b --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/IMemoable.cs b/src/Renci.SshNet/Security/BouncyCastle/util/IMemoable.cs new file mode 100644 index 000000000..9f94275f4 --- /dev/null +++ b/src/Renci.SshNet/Security/BouncyCastle/util/IMemoable.cs @@ -0,0 +1,29 @@ +using System; + +namespace Renci.SshNet.Security.Org.BouncyCastle.Utilities +{ + internal interface IMemoable + { + /// + /// Produce a copy of this object with its configuration and in its current state. + /// + /// + /// The returned object may be used simply to store the state, or may be used as a similar object + /// starting from the copied state. + /// + IMemoable Copy(); + + /// + /// Restore a copied object state into this object. + /// + /// + /// Implementations of this method should try to avoid or minimise memory allocation to perform the reset. + /// + /// an object originally {@link #copy() copied} from an object of the same type as this instance. + /// if the provided object is not of the correct type. + /// if the other parameter is in some other way invalid. + void Reset(IMemoable other); + } + +} + diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/Integers.cs b/src/Renci.SshNet/Security/BouncyCastle/util/Integers.cs new file mode 100644 index 000000000..a5c53ac80 --- /dev/null +++ b/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); + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/MemoableResetException.cs b/src/Renci.SshNet/Security/BouncyCastle/util/MemoableResetException.cs new file mode 100644 index 000000000..3b5ac38c5 --- /dev/null +++ b/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. + *

    + * The exception extends InvalidCastException to enable users to have a single handling case, + * only introducing specific handling of this one if required. + *

    + */ + internal class MemoableResetException + : InvalidCastException + { + /** + * Basic Constructor. + * + * @param msg message to be associated with this exception. + */ + public MemoableResetException(string msg) + : base(msg) + { + } + } + +} + diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/Times.cs b/src/Renci.SshNet/Security/BouncyCastle/util/Times.cs new file mode 100644 index 000000000..6ef1f65f7 --- /dev/null +++ b/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; + } + } +} diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/encoders/Hex.cs b/src/Renci.SshNet/Security/BouncyCastle/util/encoders/Hex.cs new file mode 100644 index 000000000..658eb6fec --- /dev/null +++ b/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 +{ + /// + /// Class to decode and encode Hex. + /// + 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); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/BouncyCastle/util/encoders/HexEncoder.cs b/src/Renci.SshNet/Security/BouncyCastle/util/encoders/HexEncoder.cs new file mode 100644 index 000000000..dd4e1ec20 --- /dev/null +++ b/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; + } + } +} \ No newline at end of file From 2fe968ba7c664fa39a4507d1410ee499c6fa7377 Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Tue, 4 Dec 2018 21:21:12 +0100 Subject: [PATCH 2/8] Import Chaos.NaCl for ED25519 and Curve25519 --- THIRD-PARTY-NOTICES.TXT | 29 + src/Renci.SshNet/Renci.SshNet.csproj | 65 +- .../Security/Chaos.NaCl/CryptoBytes.cs | 190 +++ .../Security/Chaos.NaCl/Ed25519.cs | 147 ++ .../Security/Chaos.NaCl/Internal/Array16.cs | 27 + .../Security/Chaos.NaCl/Internal/Array8.cs | 18 + .../Internal/ByteIntegerConverter.cs | 416 +++++ .../Internal/Ed25519Ref10/FieldElement.cs | 36 + .../Internal/Ed25519Ref10/GroupElement.cs | 63 + .../Chaos.NaCl/Internal/Ed25519Ref10/base.cs | 1357 +++++++++++++++++ .../Chaos.NaCl/Internal/Ed25519Ref10/base2.cs | 50 + .../Chaos.NaCl/Internal/Ed25519Ref10/d.cs | 9 + .../Chaos.NaCl/Internal/Ed25519Ref10/d2.cs | 9 + .../Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs | 12 + .../Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs | 13 + .../Internal/Ed25519Ref10/fe_add.cs | 63 + .../Internal/Ed25519Ref10/fe_cmov.cs | 70 + .../Internal/Ed25519Ref10/fe_cswap.cs | 78 + .../Internal/Ed25519Ref10/fe_frombytes.cs | 122 ++ .../Internal/Ed25519Ref10/fe_invert.cs | 179 +++ .../Internal/Ed25519Ref10/fe_isnegative.cs | 22 + .../Internal/Ed25519Ref10/fe_isnonzero.cs | 37 + .../Internal/Ed25519Ref10/fe_mul.cs | 258 ++++ .../Internal/Ed25519Ref10/fe_mul121666.cs | 76 + .../Internal/Ed25519Ref10/fe_neg.cs | 50 + .../Internal/Ed25519Ref10/fe_pow22523.cs | 175 +++ .../Chaos.NaCl/Internal/Ed25519Ref10/fe_sq.cs | 153 ++ .../Internal/Ed25519Ref10/fe_sq2.cs | 164 ++ .../Internal/Ed25519Ref10/fe_sub.cs | 63 + .../Internal/Ed25519Ref10/fe_tobytes.cs | 154 ++ .../Internal/Ed25519Ref10/ge_add.cs | 113 ++ .../Ed25519Ref10/ge_double_scalarmult.cs | 118 ++ .../Internal/Ed25519Ref10/ge_frombytes.cs | 54 + .../Internal/Ed25519Ref10/ge_madd.cs | 105 ++ .../Internal/Ed25519Ref10/ge_msub.cs | 104 ++ .../Internal/Ed25519Ref10/ge_p1p1_to_p2.cs | 18 + .../Internal/Ed25519Ref10/ge_p1p1_to_p3.cs | 18 + .../Internal/Ed25519Ref10/ge_p2_0.cs | 14 + .../Internal/Ed25519Ref10/ge_p2_dbl.cs | 90 ++ .../Internal/Ed25519Ref10/ge_p3_0.cs | 15 + .../Internal/Ed25519Ref10/ge_p3_dbl.cs | 17 + .../Internal/Ed25519Ref10/ge_p3_to_cached.cs | 18 + .../Internal/Ed25519Ref10/ge_p3_to_p2.cs | 17 + .../Internal/Ed25519Ref10/ge_p3_tobytes.cs | 20 + .../Internal/Ed25519Ref10/ge_precomp_0.cs | 14 + .../Ed25519Ref10/ge_scalarmult_base.cs | 113 ++ .../Internal/Ed25519Ref10/ge_sub.cs | 114 ++ .../Internal/Ed25519Ref10/ge_tobytes.cs | 20 + .../Internal/Ed25519Ref10/keypair.cs | 23 + .../Chaos.NaCl/Internal/Ed25519Ref10/open.cs | 80 + .../Internal/Ed25519Ref10/sc_clamp.cs | 14 + .../Internal/Ed25519Ref10/sc_mul_add.cs | 374 +++++ .../Internal/Ed25519Ref10/sc_reduce.cs | 263 ++++ .../Internal/Ed25519Ref10/scalarmult.cs | 205 +++ .../Chaos.NaCl/Internal/Ed25519Ref10/sign.cs | 81 + .../Internal/Ed25519Ref10/sqrtm1.cs | 9 + .../Chaos.NaCl/Internal/InternalAssert.cs | 13 + .../Chaos.NaCl/Internal/Poly1305Donna.cs | 154 ++ .../Chaos.NaCl/Internal/Salsa/Salsa20.cs | 45 + .../Chaos.NaCl/Internal/Salsa/SalsaCore.cs | 263 ++++ .../Internal/Salsa/replace regex.txt | 2 + .../Chaos.NaCl/Internal/Sha512Internal.cs | 447 ++++++ .../Security/Chaos.NaCl/License.txt | 23 + .../Chaos.NaCl/MontgomeryCurve25519.cs | 142 ++ .../Security/Chaos.NaCl/Sha512.cs | 132 ++ 65 files changed, 7356 insertions(+), 1 deletion(-) create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/CryptoBytes.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Ed25519.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array16.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array8.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/ByteIntegerConverter.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_add.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cmov.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cswap.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_frombytes.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_isnegative.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_isnonzero.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul121666.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_neg.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_pow22523.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sq.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sq2.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sub.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_tobytes.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_add.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_double_scalarmult.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_frombytes.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_msub.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p2.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p3.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p2_0.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p2_dbl.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_0.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_dbl.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_to_cached.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_to_p2.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_tobytes.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_precomp_0.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_scalarmult_base.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_sub.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_tobytes.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/open.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sc_clamp.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sc_mul_add.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sc_reduce.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/scalarmult.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sign.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/InternalAssert.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Poly1305Donna.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/Salsa20.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/SalsaCore.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/replace regex.txt create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Internal/Sha512Internal.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/License.txt create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/MontgomeryCurve25519.cs create mode 100644 src/Renci.SshNet/Security/Chaos.NaCl/Sha512.cs diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index dd1b8a210..55630a8e9 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -24,3 +24,32 @@ 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. + +License notice for Chaos.NaCl +------------------------------- + +https://github.com/CodesInChaos/Chaos.NaCl + +Public domain + +C# port + code by Christian Winnerlein (CodesInChaos) + +Poly1305 in c + written by Andrew M. (floodyberry) + original license: MIT or PUBLIC DOMAIN + https://github.com/floodyberry/poly1305-donna/blob/master/poly1305-donna-unrolled.c + +Curve25519 and Ed25519 in c + written by Dan Bernstein (djb) + public domain + from Ref10 in SUPERCOP http://bench.cr.yp.to/supercop.html + +(H)Salsa20 in c + written by Dan Bernstein (djb) + public domain + from SUPERCOP http://bench.cr.yp.to/supercop.html + +SHA512 + written by Christian Winnerlein (CodesInChaos) + public domain + directly from the specification diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 2084f956d..b0b4b8998 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -240,6 +240,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -540,6 +601,8 @@ + + - \ No newline at end of file + diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/CryptoBytes.cs b/src/Renci.SshNet/Security/Chaos.NaCl/CryptoBytes.cs new file mode 100644 index 000000000..032291827 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/CryptoBytes.cs @@ -0,0 +1,190 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Renci.SshNet.Security.Chaos.NaCl +{ + internal static class CryptoBytes + { + internal static bool ConstantTimeEquals(byte[] x, byte[] y) + { + if (x == null) + throw new ArgumentNullException("x"); + if (y == null) + throw new ArgumentNullException("y"); + if (x.Length != y.Length) + throw new ArgumentException("x.Length must equal y.Length"); + return InternalConstantTimeEquals(x, 0, y, 0, x.Length) != 0; + } + + internal static bool ConstantTimeEquals(ArraySegment x, ArraySegment y) + { + if (x.Array == null) + throw new ArgumentNullException("x.Array"); + if (y.Array == null) + throw new ArgumentNullException("y.Array"); + if (x.Count != y.Count) + throw new ArgumentException("x.Count must equal y.Count"); + + return InternalConstantTimeEquals(x.Array, x.Offset, y.Array, y.Offset, x.Count) != 0; + } + + internal static bool ConstantTimeEquals(byte[] x, int xOffset, byte[] y, int yOffset, int length) + { + if (x == null) + throw new ArgumentNullException("x"); + if (xOffset < 0) + throw new ArgumentOutOfRangeException("xOffset", "xOffset < 0"); + if (y == null) + throw new ArgumentNullException("y"); + if (yOffset < 0) + throw new ArgumentOutOfRangeException("yOffset", "yOffset < 0"); + if (length < 0) + throw new ArgumentOutOfRangeException("length", "length < 0"); + if (x.Length - xOffset < length) + throw new ArgumentException("xOffset + length > x.Length"); + if (y.Length - yOffset < length) + throw new ArgumentException("yOffset + length > y.Length"); + + return InternalConstantTimeEquals(x, xOffset, y, yOffset, length) != 0; + } + + private static uint InternalConstantTimeEquals(byte[] x, int xOffset, byte[] y, int yOffset, int length) + { + int differentbits = 0; + for (int i = 0; i < length; i++) + differentbits |= x[xOffset + i] ^ y[yOffset + i]; + return (1 & (unchecked((uint)differentbits - 1) >> 8)); + } + + internal static void Wipe(byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + InternalWipe(data, 0, data.Length); + } + + internal static void Wipe(byte[] data, int offset, int count) + { + if (data == null) + throw new ArgumentNullException("data"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + if (count < 0) + throw new ArgumentOutOfRangeException("count", "Requires count >= 0"); + if ((uint)offset + (uint)count > (uint)data.Length) + throw new ArgumentException("Requires offset + count <= data.Length"); + InternalWipe(data, offset, count); + } + + internal static void Wipe(ArraySegment data) + { + if (data.Array == null) + throw new ArgumentNullException("data.Array"); + InternalWipe(data.Array, data.Offset, data.Count); + } + + // Secure wiping is hard + // * the GC can move around and copy memory + // Perhaps this can be avoided by using unmanaged memory or by fixing the position of the array in memory + // * Swap files and error dumps can contain secret information + // It seems possible to lock memory in RAM, no idea about error dumps + // * Compiler could optimize out the wiping if it knows that data won't be read back + // I hope this is enough, suppressing inlining + // but perhaps `RtlSecureZeroMemory` is needed + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void InternalWipe(byte[] data, int offset, int count) + { + Array.Clear(data, offset, count); + } + + // shallow wipe of structs + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void InternalWipe(ref T data) + where T : struct + { + data = default(T); + } + + // constant time hex conversion + // see http://stackoverflow.com/a/14333437/445517 + // + // An explanation of the weird bit fiddling: + // + // 1. `bytes[i] >> 4` extracts the high nibble of a byte + // `bytes[i] & 0xF` extracts the low nibble of a byte + // 2. `b - 10` + // is `< 0` for values `b < 10`, which will become a decimal digit + // is `>= 0` for values `b > 10`, which will become a letter from `A` to `F`. + // 3. Using `i >> 31` on a signed 32 bit integer extracts the sign, thanks to sign extension. + // It will be `-1` for `i < 0` and `0` for `i >= 0`. + // 4. Combining 2) and 3), shows that `(b-10)>>31` will be `0` for letters and `-1` for digits. + // 5. Looking at the case for letters, the last summand becomes `0`, and `b` is in the range 10 to 15. We want to map it to `A`(65) to `F`(70), which implies adding 55 (`'A'-10`). + // 6. Looking at the case for digits, we want to adapt the last summand so it maps `b` from the range 0 to 9 to the range `0`(48) to `9`(57). This means it needs to become -7 (`'0' - 55`). + // Now we could just multiply with 7. But since -1 is represented by all bits being 1, we can instead use `& -7` since `(0 & -7) == 0` and `(-1 & -7) == -7`. + // + // Some further considerations: + // + // * I didn't use a second loop variable to index into `c`, since measurement shows that calculating it from `i` is cheaper. + // * Using exactly `i < bytes.Length` as upper bound of the loop allows the JITter to eliminate bounds checks on `bytes[i]`, so I chose that variant. + // * Making `b` an int avoids unnecessary conversions from and to byte. + internal static string ToHexStringUpper(byte[] data) + { + if (data == null) + return null; + char[] c = new char[data.Length * 2]; + int b; + for (int i = 0; i < data.Length; i++) + { + b = data[i] >> 4; + c[i * 2] = (char)(55 + b + (((b - 10) >> 31) & -7)); + b = data[i] & 0xF; + c[i * 2 + 1] = (char)(55 + b + (((b - 10) >> 31) & -7)); + } + return new string(c); + } + + // Explanation is similar to ToHexStringUpper + // constant 55 -> 87 and -7 -> -39 to compensate for the offset 32 between lowercase and uppercase letters + internal static string ToHexStringLower(byte[] data) + { + if (data == null) + return null; + char[] c = new char[data.Length * 2]; + int b; + for (int i = 0; i < data.Length; i++) + { + b = data[i] >> 4; + c[i * 2] = (char)(87 + b + (((b - 10) >> 31) & -39)); + b = data[i] & 0xF; + c[i * 2 + 1] = (char)(87 + b + (((b - 10) >> 31) & -39)); + } + return new string(c); + } + + internal static byte[] FromHexString(string hexString) + { + if (hexString == null) + return null; + if (hexString.Length % 2 != 0) + throw new FormatException("The hex string is invalid because it has an odd length"); + var result = new byte[hexString.Length / 2]; + for (int i = 0; i < result.Length; i++) + result[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); + return result; + } + + internal static string ToBase64String(byte[] data) + { + if (data == null) + return null; + return Convert.ToBase64String(data); + } + + internal static byte[] FromBase64String(string s) + { + if (s == null) + return null; + return Convert.FromBase64String(s); + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Ed25519.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Ed25519.cs new file mode 100644 index 000000000..5a085225b --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Ed25519.cs @@ -0,0 +1,147 @@ +using System; +using Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10; + +namespace Renci.SshNet.Security.Chaos.NaCl +{ + internal static class Ed25519 + { + public static readonly int PublicKeySizeInBytes = 32; + public static readonly int SignatureSizeInBytes = 64; + public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2; + public static readonly int PrivateKeySeedSizeInBytes = 32; + public static readonly int SharedKeySizeInBytes = 32; + + public static bool Verify(ArraySegment signature, ArraySegment message, ArraySegment publicKey) + { + if (signature.Count != SignatureSizeInBytes) + throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Count"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Count"); + return Ed25519Operations.crypto_sign_verify(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, publicKey.Array, publicKey.Offset); + } + + public static bool Verify(byte[] signature, byte[] message, byte[] publicKey) + { + if (signature == null) + throw new ArgumentNullException("signature"); + if (message == null) + throw new ArgumentNullException("message"); + if (publicKey == null) + throw new ArgumentNullException("publicKey"); + if (signature.Length != SignatureSizeInBytes) + throw new ArgumentException(string.Format("Signature size must be {0}", SignatureSizeInBytes), "signature.Length"); + if (publicKey.Length != PublicKeySizeInBytes) + throw new ArgumentException(string.Format("Public key size must be {0}", PublicKeySizeInBytes), "publicKey.Length"); + return Ed25519Operations.crypto_sign_verify(signature, 0, message, 0, message.Length, publicKey, 0); + } + + public static void Sign(ArraySegment signature, ArraySegment message, ArraySegment expandedPrivateKey) + { + if (signature.Array == null) + throw new ArgumentNullException("signature.Array"); + if (signature.Count != SignatureSizeInBytes) + throw new ArgumentException("signature.Count"); + if (expandedPrivateKey.Array == null) + throw new ArgumentNullException("expandedPrivateKey.Array"); + if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes) + throw new ArgumentException("expandedPrivateKey.Count"); + if (message.Array == null) + throw new ArgumentNullException("message.Array"); + Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset); + } + + public static byte[] Sign(byte[] message, byte[] expandedPrivateKey) + { + var signature = new byte[SignatureSizeInBytes]; + Sign(new ArraySegment(signature), new ArraySegment(message), new ArraySegment(expandedPrivateKey)); + return signature; + } + + public static byte[] PublicKeyFromSeed(byte[] privateKeySeed) + { + byte[] privateKey; + byte[] publicKey; + KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed); + CryptoBytes.Wipe(privateKey); + return publicKey; + } + + public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed) + { + byte[] privateKey; + byte[] publicKey; + KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed); + CryptoBytes.Wipe(publicKey); + return privateKey; + } + + public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed) + { + if (privateKeySeed == null) + throw new ArgumentNullException("privateKeySeed"); + if (privateKeySeed.Length != PrivateKeySeedSizeInBytes) + throw new ArgumentException("privateKeySeed"); + var pk = new byte[PublicKeySizeInBytes]; + var sk = new byte[ExpandedPrivateKeySizeInBytes]; + Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0); + publicKey = pk; + expandedPrivateKey = sk; + } + + public static void KeyPairFromSeed(ArraySegment publicKey, ArraySegment expandedPrivateKey, ArraySegment privateKeySeed) + { + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (expandedPrivateKey.Array == null) + throw new ArgumentNullException("expandedPrivateKey.Array"); + if (privateKeySeed.Array == null) + throw new ArgumentNullException("privateKeySeed.Array"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException("publicKey.Count"); + if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes) + throw new ArgumentException("expandedPrivateKey.Count"); + if (privateKeySeed.Count != PrivateKeySeedSizeInBytes) + throw new ArgumentException("privateKeySeed.Count"); + Ed25519Operations.crypto_sign_keypair( + publicKey.Array, publicKey.Offset, + expandedPrivateKey.Array, expandedPrivateKey.Offset, + privateKeySeed.Array, privateKeySeed.Offset); + } + + [Obsolete("Needs more testing")] + public static byte[] KeyExchange(byte[] publicKey, byte[] privateKey) + { + var sharedKey = new byte[SharedKeySizeInBytes]; + KeyExchange(new ArraySegment(sharedKey), new ArraySegment(publicKey), new ArraySegment(privateKey)); + return sharedKey; + } + + [Obsolete("Needs more testing")] + public static void KeyExchange(ArraySegment sharedKey, ArraySegment publicKey, ArraySegment privateKey) + { + if (sharedKey.Array == null) + throw new ArgumentNullException("sharedKey.Array"); + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (privateKey.Array == null) + throw new ArgumentNullException("privateKey"); + if (sharedKey.Count != 32) + throw new ArgumentException("sharedKey.Count != 32"); + if (publicKey.Count != 32) + throw new ArgumentException("publicKey.Count != 32"); + if (privateKey.Count != 64) + throw new ArgumentException("privateKey.Count != 64"); + + FieldElement montgomeryX, edwardsY, edwardsZ, sharedMontgomeryX; + FieldOperations.fe_frombytes(out edwardsY, publicKey.Array, publicKey.Offset); + FieldOperations.fe_1(out edwardsZ); + MontgomeryCurve25519.EdwardsToMontgomeryX(out montgomeryX, ref edwardsY, ref edwardsZ); + byte[] h = Sha512.Hash(privateKey.Array, privateKey.Offset, 32);//ToDo: Remove alloc + ScalarOperations.sc_clamp(h, 0); + MontgomeryOperations.scalarmult(out sharedMontgomeryX, h, 0, ref montgomeryX); + CryptoBytes.Wipe(h); + FieldOperations.fe_tobytes(sharedKey.Array, sharedKey.Offset, ref sharedMontgomeryX); + MontgomeryCurve25519.KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array16.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array16.cs new file mode 100644 index 000000000..dc2b08f5a --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array16.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal +{ + // Array16 Salsa20 state + // Array16 SHA-512 block + internal struct Array16 + { + public T x0; + public T x1; + public T x2; + public T x3; + public T x4; + public T x5; + public T x6; + public T x7; + public T x8; + public T x9; + public T x10; + public T x11; + public T x12; + public T x13; + public T x14; + public T x15; + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array8.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array8.cs new file mode 100644 index 000000000..1aeeb09cb --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Array8.cs @@ -0,0 +1,18 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal +{ + // Array8 Poly1305 key + // Array8 SHA-512 state/output + internal struct Array8 + { + public T x0; + public T x1; + public T x2; + public T x3; + public T x4; + public T x5; + public T x6; + public T x7; + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/ByteIntegerConverter.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/ByteIntegerConverter.cs new file mode 100644 index 000000000..72f4e8afb --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/ByteIntegerConverter.cs @@ -0,0 +1,416 @@ +using System; +using System.Collections.Generic; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal +{ + // Loops? Arrays? Never heard of that stuff + // Library avoids unnecessary heap allocations and unsafe code + // so this ugly code becomes necessary :( + internal static class ByteIntegerConverter + { + #region Individual + + internal static UInt32 LoadLittleEndian32(byte[] buf, int offset) + { + return + (UInt32)(buf[offset + 0]) + | (((UInt32)(buf[offset + 1])) << 8) + | (((UInt32)(buf[offset + 2])) << 16) + | (((UInt32)(buf[offset + 3])) << 24); + } + + internal static void StoreLittleEndian32(byte[] buf, int offset, UInt32 value) + { + buf[offset + 0] = unchecked((byte)value); + buf[offset + 1] = unchecked((byte)(value >> 8)); + buf[offset + 2] = unchecked((byte)(value >> 16)); + buf[offset + 3] = unchecked((byte)(value >> 24)); + } + + internal static UInt64 LoadBigEndian64(byte[] buf, int offset) + { + return + (UInt64)(buf[offset + 7]) + | (((UInt64)(buf[offset + 6])) << 8) + | (((UInt64)(buf[offset + 5])) << 16) + | (((UInt64)(buf[offset + 4])) << 24) + | (((UInt64)(buf[offset + 3])) << 32) + | (((UInt64)(buf[offset + 2])) << 40) + | (((UInt64)(buf[offset + 1])) << 48) + | (((UInt64)(buf[offset + 0])) << 56); + } + + internal static void StoreBigEndian64(byte[] buf, int offset, UInt64 value) + { + buf[offset + 7] = unchecked((byte)value); + buf[offset + 6] = unchecked((byte)(value >> 8)); + buf[offset + 5] = unchecked((byte)(value >> 16)); + buf[offset + 4] = unchecked((byte)(value >> 24)); + buf[offset + 3] = unchecked((byte)(value >> 32)); + buf[offset + 2] = unchecked((byte)(value >> 40)); + buf[offset + 1] = unchecked((byte)(value >> 48)); + buf[offset + 0] = unchecked((byte)(value >> 56)); + } + + /*internal static void XorLittleEndian32(byte[] buf, int offset, UInt32 value) + { + buf[offset + 0] ^= (byte)value; + buf[offset + 1] ^= (byte)(value >> 8); + buf[offset + 2] ^= (byte)(value >> 16); + buf[offset + 3] ^= (byte)(value >> 24); + }*/ + + /*internal static void XorLittleEndian32(byte[] output, int outputOffset, byte[] input, int inputOffset, UInt32 value) + { + output[outputOffset + 0] = (byte)(input[inputOffset + 0] ^ value); + output[outputOffset + 1] = (byte)(input[inputOffset + 1] ^ (value >> 8)); + output[outputOffset + 2] = (byte)(input[inputOffset + 2] ^ (value >> 16)); + output[outputOffset + 3] = (byte)(input[inputOffset + 3] ^ (value >> 24)); + }*/ + + #endregion + + #region Array8 + + internal static void Array8LoadLittleEndian32(out Array8 output, byte[] input, int inputOffset) + { + output.x0 = LoadLittleEndian32(input, inputOffset + 0); + output.x1 = LoadLittleEndian32(input, inputOffset + 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 8); + output.x3 = LoadLittleEndian32(input, inputOffset + 12); + output.x4 = LoadLittleEndian32(input, inputOffset + 16); + output.x5 = LoadLittleEndian32(input, inputOffset + 20); + output.x6 = LoadLittleEndian32(input, inputOffset + 24); + output.x7 = LoadLittleEndian32(input, inputOffset + 28); + } + + /* internal static void Array8LoadLittleEndian32(out Array8 output, byte[] input, int inputOffset, int inputLength) + { + #if DEBUG + if (inputLength <= 0) + throw new ArgumentException(); + #endif + int inputEnd = inputOffset + inputLength; + UInt32 highestInt; + switch (inputLength & 3) + { + case 1: + highestInt = input[inputEnd - 1]; + break; + case 2: + highestInt = (uint)( + (input[inputEnd - 1] << 8) | + (input[inputEnd - 2])); + break; + case 3: + highestInt = (uint)( + (input[inputEnd - 1] << 16) | + (input[inputEnd - 2] << 8) | + (input[inputEnd - 3])); + break; + case 0: + highestInt = (uint)( + (input[inputEnd - 1] << 24) | + (input[inputEnd - 2] << 16) | + (input[inputEnd - 3] << 8) | + (input[inputEnd - 4])); + break; + default: + throw new InvalidOperationException(); + } + switch ((inputLength - 1) >> 2) + { + case 7: + output.x7 = highestInt; + output.x6 = LoadLittleEndian32(input, inputOffset + 6 * 4); + output.x5 = LoadLittleEndian32(input, inputOffset + 5 * 4); + output.x4 = LoadLittleEndian32(input, inputOffset + 4 * 4); + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 6: + output.x7 = 0; + output.x6 = highestInt; + output.x5 = LoadLittleEndian32(input, inputOffset + 5 * 4); + output.x4 = LoadLittleEndian32(input, inputOffset + 4 * 4); + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 5: + output.x7 = 0; + output.x6 = 0; + output.x5 = highestInt; + output.x4 = LoadLittleEndian32(input, inputOffset + 4 * 4); + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 4: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = highestInt; + output.x3 = LoadLittleEndian32(input, inputOffset + 3 * 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 3: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = highestInt; + output.x2 = LoadLittleEndian32(input, inputOffset + 2 * 4); + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 2: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = 0; + output.x2 = highestInt; + output.x1 = LoadLittleEndian32(input, inputOffset + 1 * 4); + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 1: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = 0; + output.x2 = 0; + output.x1 = highestInt; + output.x0 = LoadLittleEndian32(input, inputOffset + 0 * 4); + return; + case 0: + output.x7 = 0; + output.x6 = 0; + output.x5 = 0; + output.x4 = 0; + output.x3 = 0; + output.x2 = 0; + output.x1 = 0; + output.x0 = highestInt; + return; + default: + throw new InvalidOperationException(); + } + }*/ + + /*internal static void Array8XorLittleEndian(byte[] output, int outputOffset, byte[] input, int inputOffset, ref Array8 keyStream, int length) + { +#if DEBUG + InternalAssert(length > 0); +#endif + int outputEnd = outputOffset + length; + UInt32 highestInt; + switch ((length - 1) >> 2) + { + case 7: + highestInt = keyStream.x7; + XorLittleEndian32(output, outputOffset + 6 * 4, input, inputOffset + 6 * 4, keyStream.x6); + XorLittleEndian32(output, outputOffset + 5 * 4, input, inputOffset + 6 * 4, keyStream.x5); + XorLittleEndian32(output, outputOffset + 4 * 4, input, inputOffset + 6 * 4, keyStream.x4); + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 6: + highestInt = keyStream.x6; + XorLittleEndian32(output, outputOffset + 5 * 4, input, inputOffset + 6 * 4, keyStream.x5); + XorLittleEndian32(output, outputOffset + 4 * 4, input, inputOffset + 6 * 4, keyStream.x4); + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 5: + highestInt = keyStream.x5; + XorLittleEndian32(output, outputOffset + 4 * 4, input, inputOffset + 6 * 4, keyStream.x4); + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 4: + highestInt = keyStream.x4; + XorLittleEndian32(output, outputOffset + 3 * 4, input, inputOffset + 6 * 4, keyStream.x3); + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 3: + highestInt = keyStream.x3; + XorLittleEndian32(output, outputOffset + 2 * 4, input, inputOffset + 6 * 4, keyStream.x2); + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 2: + highestInt = keyStream.x2; + XorLittleEndian32(output, outputOffset + 1 * 4, input, inputOffset + 6 * 4, keyStream.x1); + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 1: + highestInt = keyStream.x1; + XorLittleEndian32(output, outputOffset + 0 * 4, input, inputOffset + 6 * 4, keyStream.x0); + break; + case 0: + highestInt = keyStream.x0; + break; + default: + throw new InvalidOperationException(); + } + switch (length & 3) + { + case 1: + output[outputEnd - 1] ^= (byte)highestInt; + break; + case 2: + output[outputEnd - 1] ^= (byte)(highestInt >> 8); + output[outputEnd - 2] ^= (byte)highestInt; + break; + case 3: + output[outputEnd - 1] ^= (byte)(highestInt >> 16); + output[outputEnd - 2] ^= (byte)(highestInt >> 8); + output[outputEnd - 3] ^= (byte)highestInt; + break; + case 0: + output[outputEnd - 1] ^= (byte)(highestInt >> 24); + output[outputEnd - 2] ^= (byte)(highestInt >> 16); + output[outputEnd - 3] ^= (byte)(highestInt >> 8); + output[outputEnd - 4] ^= (byte)highestInt; + break; + default: + throw new InvalidOperationException(); + } + }*/ + + /*internal static void Array8StoreLittleEndian32(byte[] output, int outputOffset, ref Array8 input) + { + StoreLittleEndian32(output, outputOffset + 0, input.x0); + StoreLittleEndian32(output, outputOffset + 4, input.x1); + StoreLittleEndian32(output, outputOffset + 8, input.x2); + StoreLittleEndian32(output, outputOffset + 12, input.x3); + StoreLittleEndian32(output, outputOffset + 16, input.x4); + StoreLittleEndian32(output, outputOffset + 20, input.x5); + StoreLittleEndian32(output, outputOffset + 24, input.x6); + StoreLittleEndian32(output, outputOffset + 28, input.x7); + }*/ + #endregion + + internal static void Array16LoadBigEndian64(out Array16 output, byte[] input, int inputOffset) + { + output.x0 = LoadBigEndian64(input, inputOffset + 0); + output.x1 = LoadBigEndian64(input, inputOffset + 8); + output.x2 = LoadBigEndian64(input, inputOffset + 16); + output.x3 = LoadBigEndian64(input, inputOffset + 24); + output.x4 = LoadBigEndian64(input, inputOffset + 32); + output.x5 = LoadBigEndian64(input, inputOffset + 40); + output.x6 = LoadBigEndian64(input, inputOffset + 48); + output.x7 = LoadBigEndian64(input, inputOffset + 56); + output.x8 = LoadBigEndian64(input, inputOffset + 64); + output.x9 = LoadBigEndian64(input, inputOffset + 72); + output.x10 = LoadBigEndian64(input, inputOffset + 80); + output.x11 = LoadBigEndian64(input, inputOffset + 88); + output.x12 = LoadBigEndian64(input, inputOffset + 96); + output.x13 = LoadBigEndian64(input, inputOffset + 104); + output.x14 = LoadBigEndian64(input, inputOffset + 112); + output.x15 = LoadBigEndian64(input, inputOffset + 120); + } + + // ToDo: Only used in tests. Remove? + internal static void Array16LoadLittleEndian32(out Array16 output, byte[] input, int inputOffset) + { + output.x0 = LoadLittleEndian32(input, inputOffset + 0); + output.x1 = LoadLittleEndian32(input, inputOffset + 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 8); + output.x3 = LoadLittleEndian32(input, inputOffset + 12); + output.x4 = LoadLittleEndian32(input, inputOffset + 16); + output.x5 = LoadLittleEndian32(input, inputOffset + 20); + output.x6 = LoadLittleEndian32(input, inputOffset + 24); + output.x7 = LoadLittleEndian32(input, inputOffset + 28); + output.x8 = LoadLittleEndian32(input, inputOffset + 32); + output.x9 = LoadLittleEndian32(input, inputOffset + 36); + output.x10 = LoadLittleEndian32(input, inputOffset + 40); + output.x11 = LoadLittleEndian32(input, inputOffset + 44); + output.x12 = LoadLittleEndian32(input, inputOffset + 48); + output.x13 = LoadLittleEndian32(input, inputOffset + 52); + output.x14 = LoadLittleEndian32(input, inputOffset + 56); + output.x15 = LoadLittleEndian32(input, inputOffset + 60); + } + + /*internal static void Array16LoadLittleEndian32(out Array16 output, byte[] input, int inputOffset, int inputLength) + { + Array8 temp; + if (inputLength > 32) + { + output.x0 = LoadLittleEndian32(input, inputOffset + 0); + output.x1 = LoadLittleEndian32(input, inputOffset + 4); + output.x2 = LoadLittleEndian32(input, inputOffset + 8); + output.x3 = LoadLittleEndian32(input, inputOffset + 12); + output.x4 = LoadLittleEndian32(input, inputOffset + 16); + output.x5 = LoadLittleEndian32(input, inputOffset + 20); + output.x6 = LoadLittleEndian32(input, inputOffset + 24); + output.x7 = LoadLittleEndian32(input, inputOffset + 28); + Array8LoadLittleEndian32(out temp, input, inputOffset + 32, inputLength - 32); + output.x8 = temp.x0; + output.x9 = temp.x1; + output.x10 = temp.x2; + output.x11 = temp.x3; + output.x12 = temp.x4; + output.x13 = temp.x5; + output.x14 = temp.x6; + output.x15 = temp.x7; + } + else + { + Array8LoadLittleEndian32(out temp, input, inputOffset, inputLength); + output.x0 = temp.x0; + output.x1 = temp.x1; + output.x2 = temp.x2; + output.x3 = temp.x3; + output.x4 = temp.x4; + output.x5 = temp.x5; + output.x6 = temp.x6; + output.x7 = temp.x7; + output.x8 = 0; + output.x9 = 0; + output.x10 = 0; + output.x11 = 0; + output.x12 = 0; + output.x13 = 0; + output.x14 = 0; + output.x15 = 0; + } + }*/ + + internal static void Array16StoreLittleEndian32(byte[] output, int outputOffset, ref Array16 input) + { + StoreLittleEndian32(output, outputOffset + 0, input.x0); + StoreLittleEndian32(output, outputOffset + 4, input.x1); + StoreLittleEndian32(output, outputOffset + 8, input.x2); + StoreLittleEndian32(output, outputOffset + 12, input.x3); + StoreLittleEndian32(output, outputOffset + 16, input.x4); + StoreLittleEndian32(output, outputOffset + 20, input.x5); + StoreLittleEndian32(output, outputOffset + 24, input.x6); + StoreLittleEndian32(output, outputOffset + 28, input.x7); + StoreLittleEndian32(output, outputOffset + 32, input.x8); + StoreLittleEndian32(output, outputOffset + 36, input.x9); + StoreLittleEndian32(output, outputOffset + 40, input.x10); + StoreLittleEndian32(output, outputOffset + 44, input.x11); + StoreLittleEndian32(output, outputOffset + 48, input.x12); + StoreLittleEndian32(output, outputOffset + 52, input.x13); + StoreLittleEndian32(output, outputOffset + 56, input.x14); + StoreLittleEndian32(output, outputOffset + 60, input.x15); + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs new file mode 100644 index 000000000..c8b32488b --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs @@ -0,0 +1,36 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal struct FieldElement + { + internal int x0; + internal int x1; + internal int x2; + internal int x3; + internal int x4; + internal int x5; + internal int x6; + internal int x7; + internal int x8; + internal int x9; + + //internal static readonly FieldElement Zero = new FieldElement(); + //internal static readonly FieldElement One = new FieldElement() { x0 = 1 }; + + internal FieldElement(params int[] elements) + { + InternalAssert.Assert(elements.Length == 10, "elements.Length != 10"); + x0 = elements[0]; + x1 = elements[1]; + x2 = elements[2]; + x3 = elements[3]; + x4 = elements[4]; + x5 = elements[5]; + x6 = elements[6]; + x7 = elements[7]; + x8 = elements[8]; + x9 = elements[9]; + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs new file mode 100644 index 000000000..8203281ce --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs @@ -0,0 +1,63 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + /* + ge means group element. + + Here the group is the set of pairs (x,y) of field elements (see fe.h) + satisfying -x^2 + y^2 = 1 + d x^2y^2 + where d = -121665/121666. + + Representations: + ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge_precomp (Duif): (y+x,y-x,2dxy) + */ + + internal struct GroupElementP2 + { + public FieldElement X; + public FieldElement Y; + public FieldElement Z; + } ; + + internal struct GroupElementP3 + { + public FieldElement X; + public FieldElement Y; + public FieldElement Z; + public FieldElement T; + } ; + + internal struct GroupElementP1P1 + { + public FieldElement X; + public FieldElement Y; + public FieldElement Z; + public FieldElement T; + } ; + + internal struct GroupElementPreComp + { + public FieldElement yplusx; + public FieldElement yminusx; + public FieldElement xy2d; + + public GroupElementPreComp(FieldElement yplusx, FieldElement yminusx, FieldElement xy2d) + { + this.yplusx = yplusx; + this.yminusx = yminusx; + this.xy2d = xy2d; + } + } ; + + internal struct GroupElementCached + { + public FieldElement YplusX; + public FieldElement YminusX; + public FieldElement Z; + public FieldElement T2d; + } ; +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base.cs new file mode 100644 index 000000000..0e3359a3b --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base.cs @@ -0,0 +1,1357 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class LookupTables + { + /* base[i][j] = (j+1)*256^i*B */ + //32*8 + internal static GroupElementPreComp[][] Base = new GroupElementPreComp[][] + { + new[]{ + new GroupElementPreComp( + new FieldElement( 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 ), + new FieldElement( -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 ), + new FieldElement( -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 ) + ), + new GroupElementPreComp( + new FieldElement( -12815894,-12976347,-21581243,11784320,-25355658,-2750717,-11717903,-3814571,-358445,-10211303 ), + new FieldElement( -21703237,6903825,27185491,6451973,-29577724,-9554005,-15616551,11189268,-26829678,-5319081 ), + new FieldElement( 26966642,11152617,32442495,15396054,14353839,-12752335,-3128826,-9541118,-15472047,-4166697 ) + ), + new GroupElementPreComp( + new FieldElement( 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 ), + new FieldElement( 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 ), + new FieldElement( 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 ) + ), + new GroupElementPreComp( + new FieldElement( -17036878,13921892,10945806,-6033431,27105052,-16084379,-28926210,15006023,3284568,-6276540 ), + new FieldElement( 23599295,-8306047,-11193664,-7687416,13236774,10506355,7464579,9656445,13059162,10374397 ), + new FieldElement( 7798556,16710257,3033922,2874086,28997861,2835604,32406664,-3839045,-641708,-101325 ) + ), + new GroupElementPreComp( + new FieldElement( 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 ), + new FieldElement( 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 ), + new FieldElement( 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 ) + ), + new GroupElementPreComp( + new FieldElement( -15371964,-12862754,32573250,4720197,-26436522,5875511,-19188627,-15224819,-9818940,-12085777 ), + new FieldElement( -8549212,109983,15149363,2178705,22900618,4543417,3044240,-15689887,1762328,14866737 ), + new FieldElement( -18199695,-15951423,-10473290,1707278,-17185920,3916101,-28236412,3959421,27914454,4383652 ) + ), + new GroupElementPreComp( + new FieldElement( 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 ), + new FieldElement( -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 ), + new FieldElement( 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 ) + ), + new GroupElementPreComp( + new FieldElement( 14499471,-2729599,-33191113,-4254652,28494862,14271267,30290735,10876454,-33154098,2381726 ), + new FieldElement( -7195431,-2655363,-14730155,462251,-27724326,3941372,-6236617,3696005,-32300832,15351955 ), + new FieldElement( 27431194,8222322,16448760,-3907995,-18707002,11938355,-32961401,-2970515,29551813,10109425 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -13657040,-13155431,-31283750,11777098,21447386,6519384,-2378284,-1627556,10092783,-4764171 ), + new FieldElement( 27939166,14210322,4677035,16277044,-22964462,-12398139,-32508754,12005538,-17810127,12803510 ), + new FieldElement( 17228999,-15661624,-1233527,300140,-1224870,-11714777,30364213,-9038194,18016357,4397660 ) + ), + new GroupElementPreComp( + new FieldElement( -10958843,-7690207,4776341,-14954238,27850028,-15602212,-26619106,14544525,-17477504,982639 ), + new FieldElement( 29253598,15796703,-2863982,-9908884,10057023,3163536,7332899,-4120128,-21047696,9934963 ), + new FieldElement( 5793303,16271923,-24131614,-10116404,29188560,1206517,-14747930,4559895,-30123922,-10897950 ) + ), + new GroupElementPreComp( + new FieldElement( -27643952,-11493006,16282657,-11036493,28414021,-15012264,24191034,4541697,-13338309,5500568 ), + new FieldElement( 12650548,-1497113,9052871,11355358,-17680037,-8400164,-17430592,12264343,10874051,13524335 ), + new FieldElement( 25556948,-3045990,714651,2510400,23394682,-10415330,33119038,5080568,-22528059,5376628 ) + ), + new GroupElementPreComp( + new FieldElement( -26088264,-4011052,-17013699,-3537628,-6726793,1920897,-22321305,-9447443,4535768,1569007 ), + new FieldElement( -2255422,14606630,-21692440,-8039818,28430649,8775819,-30494562,3044290,31848280,12543772 ), + new FieldElement( -22028579,2943893,-31857513,6777306,13784462,-4292203,-27377195,-2062731,7718482,14474653 ) + ), + new GroupElementPreComp( + new FieldElement( 2385315,2454213,-22631320,46603,-4437935,-15680415,656965,-7236665,24316168,-5253567 ), + new FieldElement( 13741529,10911568,-33233417,-8603737,-20177830,-1033297,33040651,-13424532,-20729456,8321686 ), + new FieldElement( 21060490,-2212744,15712757,-4336099,1639040,10656336,23845965,-11874838,-9984458,608372 ) + ), + new GroupElementPreComp( + new FieldElement( -13672732,-15087586,-10889693,-7557059,-6036909,11305547,1123968,-6780577,27229399,23887 ), + new FieldElement( -23244140,-294205,-11744728,14712571,-29465699,-2029617,12797024,-6440308,-1633405,16678954 ), + new FieldElement( -29500620,4770662,-16054387,14001338,7830047,9564805,-1508144,-4795045,-17169265,4904953 ) + ), + new GroupElementPreComp( + new FieldElement( 24059557,14617003,19037157,-15039908,19766093,-14906429,5169211,16191880,2128236,-4326833 ), + new FieldElement( -16981152,4124966,-8540610,-10653797,30336522,-14105247,-29806336,916033,-6882542,-2986532 ), + new FieldElement( -22630907,12419372,-7134229,-7473371,-16478904,16739175,285431,2763829,15736322,4143876 ) + ), + new GroupElementPreComp( + new FieldElement( 2379352,11839345,-4110402,-5988665,11274298,794957,212801,-14594663,23527084,-16458268 ), + new FieldElement( 33431127,-11130478,-17838966,-15626900,8909499,8376530,-32625340,4087881,-15188911,-14416214 ), + new FieldElement( 1767683,7197987,-13205226,-2022635,-13091350,448826,5799055,4357868,-4774191,-16323038 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 6721966,13833823,-23523388,-1551314,26354293,-11863321,23365147,-3949732,7390890,2759800 ), + new FieldElement( 4409041,2052381,23373853,10530217,7676779,-12885954,21302353,-4264057,1244380,-12919645 ), + new FieldElement( -4421239,7169619,4982368,-2957590,30256825,-2777540,14086413,9208236,15886429,16489664 ) + ), + new GroupElementPreComp( + new FieldElement( 1996075,10375649,14346367,13311202,-6874135,-16438411,-13693198,398369,-30606455,-712933 ), + new FieldElement( -25307465,9795880,-2777414,14878809,-33531835,14780363,13348553,12076947,-30836462,5113182 ), + new FieldElement( -17770784,11797796,31950843,13929123,-25888302,12288344,-30341101,-7336386,13847711,5387222 ) + ), + new GroupElementPreComp( + new FieldElement( -18582163,-3416217,17824843,-2340966,22744343,-10442611,8763061,3617786,-19600662,10370991 ), + new FieldElement( 20246567,-14369378,22358229,-543712,18507283,-10413996,14554437,-8746092,32232924,16763880 ), + new FieldElement( 9648505,10094563,26416693,14745928,-30374318,-6472621,11094161,15689506,3140038,-16510092 ) + ), + new GroupElementPreComp( + new FieldElement( -16160072,5472695,31895588,4744994,8823515,10365685,-27224800,9448613,-28774454,366295 ), + new FieldElement( 19153450,11523972,-11096490,-6503142,-24647631,5420647,28344573,8041113,719605,11671788 ), + new FieldElement( 8678025,2694440,-6808014,2517372,4964326,11152271,-15432916,-15266516,27000813,-10195553 ) + ), + new GroupElementPreComp( + new FieldElement( -15157904,7134312,8639287,-2814877,-7235688,10421742,564065,5336097,6750977,-14521026 ), + new FieldElement( 11836410,-3979488,26297894,16080799,23455045,15735944,1695823,-8819122,8169720,16220347 ), + new FieldElement( -18115838,8653647,17578566,-6092619,-8025777,-16012763,-11144307,-2627664,-5990708,-14166033 ) + ), + new GroupElementPreComp( + new FieldElement( -23308498,-10968312,15213228,-10081214,-30853605,-11050004,27884329,2847284,2655861,1738395 ), + new FieldElement( -27537433,-14253021,-25336301,-8002780,-9370762,8129821,21651608,-3239336,-19087449,-11005278 ), + new FieldElement( 1533110,3437855,23735889,459276,29970501,11335377,26030092,5821408,10478196,8544890 ) + ), + new GroupElementPreComp( + new FieldElement( 32173121,-16129311,24896207,3921497,22579056,-3410854,19270449,12217473,17789017,-3395995 ), + new FieldElement( -30552961,-2228401,-15578829,-10147201,13243889,517024,15479401,-3853233,30460520,1052596 ), + new FieldElement( -11614875,13323618,32618793,8175907,-15230173,12596687,27491595,-4612359,3179268,-9478891 ) + ), + new GroupElementPreComp( + new FieldElement( 31947069,-14366651,-4640583,-15339921,-15125977,-6039709,-14756777,-16411740,19072640,-9511060 ), + new FieldElement( 11685058,11822410,3158003,-13952594,33402194,-4165066,5977896,-5215017,473099,5040608 ), + new FieldElement( -20290863,8198642,-27410132,11602123,1290375,-2799760,28326862,1721092,-19558642,-3131606 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 7881532,10687937,7578723,7738378,-18951012,-2553952,21820786,8076149,-27868496,11538389 ), + new FieldElement( -19935666,3899861,18283497,-6801568,-15728660,-11249211,8754525,7446702,-5676054,5797016 ), + new FieldElement( -11295600,-3793569,-15782110,-7964573,12708869,-8456199,2014099,-9050574,-2369172,-5877341 ) + ), + new GroupElementPreComp( + new FieldElement( -22472376,-11568741,-27682020,1146375,18956691,16640559,1192730,-3714199,15123619,10811505 ), + new FieldElement( 14352098,-3419715,-18942044,10822655,32750596,4699007,-70363,15776356,-28886779,-11974553 ), + new FieldElement( -28241164,-8072475,-4978962,-5315317,29416931,1847569,-20654173,-16484855,4714547,-9600655 ) + ), + new GroupElementPreComp( + new FieldElement( 15200332,8368572,19679101,15970074,-31872674,1959451,24611599,-4543832,-11745876,12340220 ), + new FieldElement( 12876937,-10480056,33134381,6590940,-6307776,14872440,9613953,8241152,15370987,9608631 ), + new FieldElement( -4143277,-12014408,8446281,-391603,4407738,13629032,-7724868,15866074,-28210621,-8814099 ) + ), + new GroupElementPreComp( + new FieldElement( 26660628,-15677655,8393734,358047,-7401291,992988,-23904233,858697,20571223,8420556 ), + new FieldElement( 14620715,13067227,-15447274,8264467,14106269,15080814,33531827,12516406,-21574435,-12476749 ), + new FieldElement( 236881,10476226,57258,-14677024,6472998,2466984,17258519,7256740,8791136,15069930 ) + ), + new GroupElementPreComp( + new FieldElement( 1276410,-9371918,22949635,-16322807,-23493039,-5702186,14711875,4874229,-30663140,-2331391 ), + new FieldElement( 5855666,4990204,-13711848,7294284,-7804282,1924647,-1423175,-7912378,-33069337,9234253 ), + new FieldElement( 20590503,-9018988,31529744,-7352666,-2706834,10650548,31559055,-11609587,18979186,13396066 ) + ), + new GroupElementPreComp( + new FieldElement( 24474287,4968103,22267082,4407354,24063882,-8325180,-18816887,13594782,33514650,7021958 ), + new FieldElement( -11566906,-6565505,-21365085,15928892,-26158305,4315421,-25948728,-3916677,-21480480,12868082 ), + new FieldElement( -28635013,13504661,19988037,-2132761,21078225,6443208,-21446107,2244500,-12455797,-8089383 ) + ), + new GroupElementPreComp( + new FieldElement( -30595528,13793479,-5852820,319136,-25723172,-6263899,33086546,8957937,-15233648,5540521 ), + new FieldElement( -11630176,-11503902,-8119500,-7643073,2620056,1022908,-23710744,-1568984,-16128528,-14962807 ), + new FieldElement( 23152971,775386,27395463,14006635,-9701118,4649512,1689819,892185,-11513277,-15205948 ) + ), + new GroupElementPreComp( + new FieldElement( 9770129,9586738,26496094,4324120,1556511,-3550024,27453819,4763127,-19179614,5867134 ), + new FieldElement( -32765025,1927590,31726409,-4753295,23962434,-16019500,27846559,5931263,-29749703,-16108455 ), + new FieldElement( 27461885,-2977536,22380810,1815854,-23033753,-3031938,7283490,-15148073,-19526700,7734629 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -8010264,-9590817,-11120403,6196038,29344158,-13430885,7585295,-3176626,18549497,15302069 ), + new FieldElement( -32658337,-6171222,-7672793,-11051681,6258878,13504381,10458790,-6418461,-8872242,8424746 ), + new FieldElement( 24687205,8613276,-30667046,-3233545,1863892,-1830544,19206234,7134917,-11284482,-828919 ) + ), + new GroupElementPreComp( + new FieldElement( 11334899,-9218022,8025293,12707519,17523892,-10476071,10243738,-14685461,-5066034,16498837 ), + new FieldElement( 8911542,6887158,-9584260,-6958590,11145641,-9543680,17303925,-14124238,6536641,10543906 ), + new FieldElement( -28946384,15479763,-17466835,568876,-1497683,11223454,-2669190,-16625574,-27235709,8876771 ) + ), + new GroupElementPreComp( + new FieldElement( -25742899,-12566864,-15649966,-846607,-33026686,-796288,-33481822,15824474,-604426,-9039817 ), + new FieldElement( 10330056,70051,7957388,-9002667,9764902,15609756,27698697,-4890037,1657394,3084098 ), + new FieldElement( 10477963,-7470260,12119566,-13250805,29016247,-5365589,31280319,14396151,-30233575,15272409 ) + ), + new GroupElementPreComp( + new FieldElement( -12288309,3169463,28813183,16658753,25116432,-5630466,-25173957,-12636138,-25014757,1950504 ), + new FieldElement( -26180358,9489187,11053416,-14746161,-31053720,5825630,-8384306,-8767532,15341279,8373727 ), + new FieldElement( 28685821,7759505,-14378516,-12002860,-31971820,4079242,298136,-10232602,-2878207,15190420 ) + ), + new GroupElementPreComp( + new FieldElement( -32932876,13806336,-14337485,-15794431,-24004620,10940928,8669718,2742393,-26033313,-6875003 ), + new FieldElement( -1580388,-11729417,-25979658,-11445023,-17411874,-10912854,9291594,-16247779,-12154742,6048605 ), + new FieldElement( -30305315,14843444,1539301,11864366,20201677,1900163,13934231,5128323,11213262,9168384 ) + ), + new GroupElementPreComp( + new FieldElement( -26280513,11007847,19408960,-940758,-18592965,-4328580,-5088060,-11105150,20470157,-16398701 ), + new FieldElement( -23136053,9282192,14855179,-15390078,-7362815,-14408560,-22783952,14461608,14042978,5230683 ), + new FieldElement( 29969567,-2741594,-16711867,-8552442,9175486,-2468974,21556951,3506042,-5933891,-12449708 ) + ), + new GroupElementPreComp( + new FieldElement( -3144746,8744661,19704003,4581278,-20430686,6830683,-21284170,8971513,-28539189,15326563 ), + new FieldElement( -19464629,10110288,-17262528,-3503892,-23500387,1355669,-15523050,15300988,-20514118,9168260 ), + new FieldElement( -5353335,4488613,-23803248,16314347,7780487,-15638939,-28948358,9601605,33087103,-9011387 ) + ), + new GroupElementPreComp( + new FieldElement( -19443170,-15512900,-20797467,-12445323,-29824447,10229461,-27444329,-15000531,-5996870,15664672 ), + new FieldElement( 23294591,-16632613,-22650781,-8470978,27844204,11461195,13099750,-2460356,18151676,13417686 ), + new FieldElement( -24722913,-4176517,-31150679,5988919,-26858785,6685065,1661597,-12551441,15271676,-15452665 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 11433042,-13228665,8239631,-5279517,-1985436,-725718,-18698764,2167544,-6921301,-13440182 ), + new FieldElement( -31436171,15575146,30436815,12192228,-22463353,9395379,-9917708,-8638997,12215110,12028277 ), + new FieldElement( 14098400,6555944,23007258,5757252,-15427832,-12950502,30123440,4617780,-16900089,-655628 ) + ), + new GroupElementPreComp( + new FieldElement( -4026201,-15240835,11893168,13718664,-14809462,1847385,-15819999,10154009,23973261,-12684474 ), + new FieldElement( -26531820,-3695990,-1908898,2534301,-31870557,-16550355,18341390,-11419951,32013174,-10103539 ), + new FieldElement( -25479301,10876443,-11771086,-14625140,-12369567,1838104,21911214,6354752,4425632,-837822 ) + ), + new GroupElementPreComp( + new FieldElement( -10433389,-14612966,22229858,-3091047,-13191166,776729,-17415375,-12020462,4725005,14044970 ), + new FieldElement( 19268650,-7304421,1555349,8692754,-21474059,-9910664,6347390,-1411784,-19522291,-16109756 ), + new FieldElement( -24864089,12986008,-10898878,-5558584,-11312371,-148526,19541418,8180106,9282262,10282508 ) + ), + new GroupElementPreComp( + new FieldElement( -26205082,4428547,-8661196,-13194263,4098402,-14165257,15522535,8372215,5542595,-10702683 ), + new FieldElement( -10562541,14895633,26814552,-16673850,-17480754,-2489360,-2781891,6993761,-18093885,10114655 ), + new FieldElement( -20107055,-929418,31422704,10427861,-7110749,6150669,-29091755,-11529146,25953725,-106158 ) + ), + new GroupElementPreComp( + new FieldElement( -4234397,-8039292,-9119125,3046000,2101609,-12607294,19390020,6094296,-3315279,12831125 ), + new FieldElement( -15998678,7578152,5310217,14408357,-33548620,-224739,31575954,6326196,7381791,-2421839 ), + new FieldElement( -20902779,3296811,24736065,-16328389,18374254,7318640,6295303,8082724,-15362489,12339664 ) + ), + new GroupElementPreComp( + new FieldElement( 27724736,2291157,6088201,-14184798,1792727,5857634,13848414,15768922,25091167,14856294 ), + new FieldElement( -18866652,8331043,24373479,8541013,-701998,-9269457,12927300,-12695493,-22182473,-9012899 ), + new FieldElement( -11423429,-5421590,11632845,3405020,30536730,-11674039,-27260765,13866390,30146206,9142070 ) + ), + new GroupElementPreComp( + new FieldElement( 3924129,-15307516,-13817122,-10054960,12291820,-668366,-27702774,9326384,-8237858,4171294 ), + new FieldElement( -15921940,16037937,6713787,16606682,-21612135,2790944,26396185,3731949,345228,-5462949 ), + new FieldElement( -21327538,13448259,25284571,1143661,20614966,-8849387,2031539,-12391231,-16253183,-13582083 ) + ), + new GroupElementPreComp( + new FieldElement( 31016211,-16722429,26371392,-14451233,-5027349,14854137,17477601,3842657,28012650,-16405420 ), + new FieldElement( -5075835,9368966,-8562079,-4600902,-15249953,6970560,-9189873,16292057,-8867157,3507940 ), + new FieldElement( 29439664,3537914,23333589,6997794,-17555561,-11018068,-15209202,-15051267,-9164929,6580396 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -12185861,-7679788,16438269,10826160,-8696817,-6235611,17860444,-9273846,-2095802,9304567 ), + new FieldElement( 20714564,-4336911,29088195,7406487,11426967,-5095705,14792667,-14608617,5289421,-477127 ), + new FieldElement( -16665533,-10650790,-6160345,-13305760,9192020,-1802462,17271490,12349094,26939669,-3752294 ) + ), + new GroupElementPreComp( + new FieldElement( -12889898,9373458,31595848,16374215,21471720,13221525,-27283495,-12348559,-3698806,117887 ), + new FieldElement( 22263325,-6560050,3984570,-11174646,-15114008,-566785,28311253,5358056,-23319780,541964 ), + new FieldElement( 16259219,3261970,2309254,-15534474,-16885711,-4581916,24134070,-16705829,-13337066,-13552195 ) + ), + new GroupElementPreComp( + new FieldElement( 9378160,-13140186,-22845982,-12745264,28198281,-7244098,-2399684,-717351,690426,14876244 ), + new FieldElement( 24977353,-314384,-8223969,-13465086,28432343,-1176353,-13068804,-12297348,-22380984,6618999 ), + new FieldElement( -1538174,11685646,12944378,13682314,-24389511,-14413193,8044829,-13817328,32239829,-5652762 ) + ), + new GroupElementPreComp( + new FieldElement( -18603066,4762990,-926250,8885304,-28412480,-3187315,9781647,-10350059,32779359,5095274 ), + new FieldElement( -33008130,-5214506,-32264887,-3685216,9460461,-9327423,-24601656,14506724,21639561,-2630236 ), + new FieldElement( -16400943,-13112215,25239338,15531969,3987758,-4499318,-1289502,-6863535,17874574,558605 ) + ), + new GroupElementPreComp( + new FieldElement( -13600129,10240081,9171883,16131053,-20869254,9599700,33499487,5080151,2085892,5119761 ), + new FieldElement( -22205145,-2519528,-16381601,414691,-25019550,2170430,30634760,-8363614,-31999993,-5759884 ), + new FieldElement( -6845704,15791202,8550074,-1312654,29928809,-12092256,27534430,-7192145,-22351378,12961482 ) + ), + new GroupElementPreComp( + new FieldElement( -24492060,-9570771,10368194,11582341,-23397293,-2245287,16533930,8206996,-30194652,-5159638 ), + new FieldElement( -11121496,-3382234,2307366,6362031,-135455,8868177,-16835630,7031275,7589640,8945490 ), + new FieldElement( -32152748,8917967,6661220,-11677616,-1192060,-15793393,7251489,-11182180,24099109,-14456170 ) + ), + new GroupElementPreComp( + new FieldElement( 5019558,-7907470,4244127,-14714356,-26933272,6453165,-19118182,-13289025,-6231896,-10280736 ), + new FieldElement( 10853594,10721687,26480089,5861829,-22995819,1972175,-1866647,-10557898,-3363451,-6441124 ), + new FieldElement( -17002408,5906790,221599,-6563147,7828208,-13248918,24362661,-2008168,-13866408,7421392 ) + ), + new GroupElementPreComp( + new FieldElement( 8139927,-6546497,32257646,-5890546,30375719,1886181,-21175108,15441252,28826358,-4123029 ), + new FieldElement( 6267086,9695052,7709135,-16603597,-32869068,-1886135,14795160,-7840124,13746021,-1742048 ), + new FieldElement( 28584902,7787108,-6732942,-15050729,22846041,-7571236,-3181936,-363524,4771362,-8419958 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 24949256,6376279,-27466481,-8174608,-18646154,-9930606,33543569,-12141695,3569627,11342593 ), + new FieldElement( 26514989,4740088,27912651,3697550,19331575,-11472339,6809886,4608608,7325975,-14801071 ), + new FieldElement( -11618399,-14554430,-24321212,7655128,-1369274,5214312,-27400540,10258390,-17646694,-8186692 ) + ), + new GroupElementPreComp( + new FieldElement( 11431204,15823007,26570245,14329124,18029990,4796082,-31446179,15580664,9280358,-3973687 ), + new FieldElement( -160783,-10326257,-22855316,-4304997,-20861367,-13621002,-32810901,-11181622,-15545091,4387441 ), + new FieldElement( -20799378,12194512,3937617,-5805892,-27154820,9340370,-24513992,8548137,20617071,-7482001 ) + ), + new GroupElementPreComp( + new FieldElement( -938825,-3930586,-8714311,16124718,24603125,-6225393,-13775352,-11875822,24345683,10325460 ), + new FieldElement( -19855277,-1568885,-22202708,8714034,14007766,6928528,16318175,-1010689,4766743,3552007 ), + new FieldElement( -21751364,-16730916,1351763,-803421,-4009670,3950935,3217514,14481909,10988822,-3994762 ) + ), + new GroupElementPreComp( + new FieldElement( 15564307,-14311570,3101243,5684148,30446780,-8051356,12677127,-6505343,-8295852,13296005 ), + new FieldElement( -9442290,6624296,-30298964,-11913677,-4670981,-2057379,31521204,9614054,-30000824,12074674 ), + new FieldElement( 4771191,-135239,14290749,-13089852,27992298,14998318,-1413936,-1556716,29832613,-16391035 ) + ), + new GroupElementPreComp( + new FieldElement( 7064884,-7541174,-19161962,-5067537,-18891269,-2912736,25825242,5293297,-27122660,13101590 ), + new FieldElement( -2298563,2439670,-7466610,1719965,-27267541,-16328445,32512469,-5317593,-30356070,-4190957 ), + new FieldElement( -30006540,10162316,-33180176,3981723,-16482138,-13070044,14413974,9515896,19568978,9628812 ) + ), + new GroupElementPreComp( + new FieldElement( 33053803,199357,15894591,1583059,27380243,-4580435,-17838894,-6106839,-6291786,3437740 ), + new FieldElement( -18978877,3884493,19469877,12726490,15913552,13614290,-22961733,70104,7463304,4176122 ), + new FieldElement( -27124001,10659917,11482427,-16070381,12771467,-6635117,-32719404,-5322751,24216882,5944158 ) + ), + new GroupElementPreComp( + new FieldElement( 8894125,7450974,-2664149,-9765752,-28080517,-12389115,19345746,14680796,11632993,5847885 ), + new FieldElement( 26942781,-2315317,9129564,-4906607,26024105,11769399,-11518837,6367194,-9727230,4782140 ), + new FieldElement( 19916461,-4828410,-22910704,-11414391,25606324,-5972441,33253853,8220911,6358847,-1873857 ) + ), + new GroupElementPreComp( + new FieldElement( 801428,-2081702,16569428,11065167,29875704,96627,7908388,-4480480,-13538503,1387155 ), + new FieldElement( 19646058,5720633,-11416706,12814209,11607948,12749789,14147075,15156355,-21866831,11835260 ), + new FieldElement( 19299512,1155910,28703737,14890794,2925026,7269399,26121523,15467869,-26560550,5052483 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -3017432,10058206,1980837,3964243,22160966,12322533,-6431123,-12618185,12228557,-7003677 ), + new FieldElement( 32944382,14922211,-22844894,5188528,21913450,-8719943,4001465,13238564,-6114803,8653815 ), + new FieldElement( 22865569,-4652735,27603668,-12545395,14348958,8234005,24808405,5719875,28483275,2841751 ) + ), + new GroupElementPreComp( + new FieldElement( -16420968,-1113305,-327719,-12107856,21886282,-15552774,-1887966,-315658,19932058,-12739203 ), + new FieldElement( -11656086,10087521,-8864888,-5536143,-19278573,-3055912,3999228,13239134,-4777469,-13910208 ), + new FieldElement( 1382174,-11694719,17266790,9194690,-13324356,9720081,20403944,11284705,-14013818,3093230 ) + ), + new GroupElementPreComp( + new FieldElement( 16650921,-11037932,-1064178,1570629,-8329746,7352753,-302424,16271225,-24049421,-6691850 ), + new FieldElement( -21911077,-5927941,-4611316,-5560156,-31744103,-10785293,24123614,15193618,-21652117,-16739389 ), + new FieldElement( -9935934,-4289447,-25279823,4372842,2087473,10399484,31870908,14690798,17361620,11864968 ) + ), + new GroupElementPreComp( + new FieldElement( -11307610,6210372,13206574,5806320,-29017692,-13967200,-12331205,-7486601,-25578460,-16240689 ), + new FieldElement( 14668462,-12270235,26039039,15305210,25515617,4542480,10453892,6577524,9145645,-6443880 ), + new FieldElement( 5974874,3053895,-9433049,-10385191,-31865124,3225009,-7972642,3936128,-5652273,-3050304 ) + ), + new GroupElementPreComp( + new FieldElement( 30625386,-4729400,-25555961,-12792866,-20484575,7695099,17097188,-16303496,-27999779,1803632 ), + new FieldElement( -3553091,9865099,-5228566,4272701,-5673832,-16689700,14911344,12196514,-21405489,7047412 ), + new FieldElement( 20093277,9920966,-11138194,-5343857,13161587,12044805,-32856851,4124601,-32343828,-10257566 ) + ), + new GroupElementPreComp( + new FieldElement( -20788824,14084654,-13531713,7842147,19119038,-13822605,4752377,-8714640,-21679658,2288038 ), + new FieldElement( -26819236,-3283715,29965059,3039786,-14473765,2540457,29457502,14625692,-24819617,12570232 ), + new FieldElement( -1063558,-11551823,16920318,12494842,1278292,-5869109,-21159943,-3498680,-11974704,4724943 ) + ), + new GroupElementPreComp( + new FieldElement( 17960970,-11775534,-4140968,-9702530,-8876562,-1410617,-12907383,-8659932,-29576300,1903856 ), + new FieldElement( 23134274,-14279132,-10681997,-1611936,20684485,15770816,-12989750,3190296,26955097,14109738 ), + new FieldElement( 15308788,5320727,-30113809,-14318877,22902008,7767164,29425325,-11277562,31960942,11934971 ) + ), + new GroupElementPreComp( + new FieldElement( -27395711,8435796,4109644,12222639,-24627868,14818669,20638173,4875028,10491392,1379718 ), + new FieldElement( -13159415,9197841,3875503,-8936108,-1383712,-5879801,33518459,16176658,21432314,12180697 ), + new FieldElement( -11787308,11500838,13787581,-13832590,-22430679,10140205,1465425,12689540,-10301319,-13872883 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 5414091,-15386041,-21007664,9643570,12834970,1186149,-2622916,-1342231,26128231,6032912 ), + new FieldElement( -26337395,-13766162,32496025,-13653919,17847801,-12669156,3604025,8316894,-25875034,-10437358 ), + new FieldElement( 3296484,6223048,24680646,-12246460,-23052020,5903205,-8862297,-4639164,12376617,3188849 ) + ), + new GroupElementPreComp( + new FieldElement( 29190488,-14659046,27549113,-1183516,3520066,-10697301,32049515,-7309113,-16109234,-9852307 ), + new FieldElement( -14744486,-9309156,735818,-598978,-20407687,-5057904,25246078,-15795669,18640741,-960977 ), + new FieldElement( -6928835,-16430795,10361374,5642961,4910474,12345252,-31638386,-494430,10530747,1053335 ) + ), + new GroupElementPreComp( + new FieldElement( -29265967,-14186805,-13538216,-12117373,-19457059,-10655384,-31462369,-2948985,24018831,15026644 ), + new FieldElement( -22592535,-3145277,-2289276,5953843,-13440189,9425631,25310643,13003497,-2314791,-15145616 ), + new FieldElement( -27419985,-603321,-8043984,-1669117,-26092265,13987819,-27297622,187899,-23166419,-2531735 ) + ), + new GroupElementPreComp( + new FieldElement( -21744398,-13810475,1844840,5021428,-10434399,-15911473,9716667,16266922,-5070217,726099 ), + new FieldElement( 29370922,-6053998,7334071,-15342259,9385287,2247707,-13661962,-4839461,30007388,-15823341 ), + new FieldElement( -936379,16086691,23751945,-543318,-1167538,-5189036,9137109,730663,9835848,4555336 ) + ), + new GroupElementPreComp( + new FieldElement( -23376435,1410446,-22253753,-12899614,30867635,15826977,17693930,544696,-11985298,12422646 ), + new FieldElement( 31117226,-12215734,-13502838,6561947,-9876867,-12757670,-5118685,-4096706,29120153,13924425 ), + new FieldElement( -17400879,-14233209,19675799,-2734756,-11006962,-5858820,-9383939,-11317700,7240931,-237388 ) + ), + new GroupElementPreComp( + new FieldElement( -31361739,-11346780,-15007447,-5856218,-22453340,-12152771,1222336,4389483,3293637,-15551743 ), + new FieldElement( -16684801,-14444245,11038544,11054958,-13801175,-3338533,-24319580,7733547,12796905,-6335822 ), + new FieldElement( -8759414,-10817836,-25418864,10783769,-30615557,-9746811,-28253339,3647836,3222231,-11160462 ) + ), + new GroupElementPreComp( + new FieldElement( 18606113,1693100,-25448386,-15170272,4112353,10045021,23603893,-2048234,-7550776,2484985 ), + new FieldElement( 9255317,-3131197,-12156162,-1004256,13098013,-9214866,16377220,-2102812,-19802075,-3034702 ), + new FieldElement( -22729289,7496160,-5742199,11329249,19991973,-3347502,-31718148,9936966,-30097688,-10618797 ) + ), + new GroupElementPreComp( + new FieldElement( 21878590,-5001297,4338336,13643897,-3036865,13160960,19708896,5415497,-7360503,-4109293 ), + new FieldElement( 27736861,10103576,12500508,8502413,-3413016,-9633558,10436918,-1550276,-23659143,-8132100 ), + new FieldElement( 19492550,-12104365,-29681976,-852630,-3208171,12403437,30066266,8367329,13243957,8709688 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 12015105,2801261,28198131,10151021,24818120,-4743133,-11194191,-5645734,5150968,7274186 ), + new FieldElement( 2831366,-12492146,1478975,6122054,23825128,-12733586,31097299,6083058,31021603,-9793610 ), + new FieldElement( -2529932,-2229646,445613,10720828,-13849527,-11505937,-23507731,16354465,15067285,-14147707 ) + ), + new GroupElementPreComp( + new FieldElement( 7840942,14037873,-33364863,15934016,-728213,-3642706,21403988,1057586,-19379462,-12403220 ), + new FieldElement( 915865,-16469274,15608285,-8789130,-24357026,6060030,-17371319,8410997,-7220461,16527025 ), + new FieldElement( 32922597,-556987,20336074,-16184568,10903705,-5384487,16957574,52992,23834301,6588044 ) + ), + new GroupElementPreComp( + new FieldElement( 32752030,11232950,3381995,-8714866,22652988,-10744103,17159699,16689107,-20314580,-1305992 ), + new FieldElement( -4689649,9166776,-25710296,-10847306,11576752,12733943,7924251,-2752281,1976123,-7249027 ), + new FieldElement( 21251222,16309901,-2983015,-6783122,30810597,12967303,156041,-3371252,12331345,-8237197 ) + ), + new GroupElementPreComp( + new FieldElement( 8651614,-4477032,-16085636,-4996994,13002507,2950805,29054427,-5106970,10008136,-4667901 ), + new FieldElement( 31486080,15114593,-14261250,12951354,14369431,-7387845,16347321,-13662089,8684155,-10532952 ), + new FieldElement( 19443825,11385320,24468943,-9659068,-23919258,2187569,-26263207,-6086921,31316348,14219878 ) + ), + new GroupElementPreComp( + new FieldElement( -28594490,1193785,32245219,11392485,31092169,15722801,27146014,6992409,29126555,9207390 ), + new FieldElement( 32382935,1110093,18477781,11028262,-27411763,-7548111,-4980517,10843782,-7957600,-14435730 ), + new FieldElement( 2814918,7836403,27519878,-7868156,-20894015,-11553689,-21494559,8550130,28346258,1994730 ) + ), + new GroupElementPreComp( + new FieldElement( -19578299,8085545,-14000519,-3948622,2785838,-16231307,-19516951,7174894,22628102,8115180 ), + new FieldElement( -30405132,955511,-11133838,-15078069,-32447087,-13278079,-25651578,3317160,-9943017,930272 ), + new FieldElement( -15303681,-6833769,28856490,1357446,23421993,1057177,24091212,-1388970,-22765376,-10650715 ) + ), + new GroupElementPreComp( + new FieldElement( -22751231,-5303997,-12907607,-12768866,-15811511,-7797053,-14839018,-16554220,-1867018,8398970 ), + new FieldElement( -31969310,2106403,-4736360,1362501,12813763,16200670,22981545,-6291273,18009408,-15772772 ), + new FieldElement( -17220923,-9545221,-27784654,14166835,29815394,7444469,29551787,-3727419,19288549,1325865 ) + ), + new GroupElementPreComp( + new FieldElement( 15100157,-15835752,-23923978,-1005098,-26450192,15509408,12376730,-3479146,33166107,-8042750 ), + new FieldElement( 20909231,13023121,-9209752,16251778,-5778415,-8094914,12412151,10018715,2213263,-13878373 ), + new FieldElement( 32529814,-11074689,30361439,-16689753,-9135940,1513226,22922121,6382134,-5766928,8371348 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 9923462,11271500,12616794,3544722,-29998368,-1721626,12891687,-8193132,-26442943,10486144 ), + new FieldElement( -22597207,-7012665,8587003,-8257861,4084309,-12970062,361726,2610596,-23921530,-11455195 ), + new FieldElement( 5408411,-1136691,-4969122,10561668,24145918,14240566,31319731,-4235541,19985175,-3436086 ) + ), + new GroupElementPreComp( + new FieldElement( -13994457,16616821,14549246,3341099,32155958,13648976,-17577068,8849297,65030,8370684 ), + new FieldElement( -8320926,-12049626,31204563,5839400,-20627288,-1057277,-19442942,6922164,12743482,-9800518 ), + new FieldElement( -2361371,12678785,28815050,4759974,-23893047,4884717,23783145,11038569,18800704,255233 ) + ), + new GroupElementPreComp( + new FieldElement( -5269658,-1773886,13957886,7990715,23132995,728773,13393847,9066957,19258688,-14753793 ), + new FieldElement( -2936654,-10827535,-10432089,14516793,-3640786,4372541,-31934921,2209390,-1524053,2055794 ), + new FieldElement( 580882,16705327,5468415,-2683018,-30926419,-14696000,-7203346,-8994389,-30021019,7394435 ) + ), + new GroupElementPreComp( + new FieldElement( 23838809,1822728,-15738443,15242727,8318092,-3733104,-21672180,-3492205,-4821741,14799921 ), + new FieldElement( 13345610,9759151,3371034,-16137791,16353039,8577942,31129804,13496856,-9056018,7402518 ), + new FieldElement( 2286874,-4435931,-20042458,-2008336,-13696227,5038122,11006906,-15760352,8205061,1607563 ) + ), + new GroupElementPreComp( + new FieldElement( 14414086,-8002132,3331830,-3208217,22249151,-5594188,18364661,-2906958,30019587,-9029278 ), + new FieldElement( -27688051,1585953,-10775053,931069,-29120221,-11002319,-14410829,12029093,9944378,8024 ), + new FieldElement( 4368715,-3709630,29874200,-15022983,-20230386,-11410704,-16114594,-999085,-8142388,5640030 ) + ), + new GroupElementPreComp( + new FieldElement( 10299610,13746483,11661824,16234854,7630238,5998374,9809887,-16694564,15219798,-14327783 ), + new FieldElement( 27425505,-5719081,3055006,10660664,23458024,595578,-15398605,-1173195,-18342183,9742717 ), + new FieldElement( 6744077,2427284,26042789,2720740,-847906,1118974,32324614,7406442,12420155,1994844 ) + ), + new GroupElementPreComp( + new FieldElement( 14012521,-5024720,-18384453,-9578469,-26485342,-3936439,-13033478,-10909803,24319929,-6446333 ), + new FieldElement( 16412690,-4507367,10772641,15929391,-17068788,-4658621,10555945,-10484049,-30102368,-4739048 ), + new FieldElement( 22397382,-7767684,-9293161,-12792868,17166287,-9755136,-27333065,6199366,21880021,-12250760 ) + ), + new GroupElementPreComp( + new FieldElement( -4283307,5368523,-31117018,8163389,-30323063,3209128,16557151,8890729,8840445,4957760 ), + new FieldElement( -15447727,709327,-6919446,-10870178,-29777922,6522332,-21720181,12130072,-14796503,5005757 ), + new FieldElement( -2114751,-14308128,23019042,15765735,-25269683,6002752,10183197,-13239326,-16395286,-2176112 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -19025756,1632005,13466291,-7995100,-23640451,16573537,-32013908,-3057104,22208662,2000468 ), + new FieldElement( 3065073,-1412761,-25598674,-361432,-17683065,-5703415,-8164212,11248527,-3691214,-7414184 ), + new FieldElement( 10379208,-6045554,8877319,1473647,-29291284,-12507580,16690915,2553332,-3132688,16400289 ) + ), + new GroupElementPreComp( + new FieldElement( 15716668,1254266,-18472690,7446274,-8448918,6344164,-22097271,-7285580,26894937,9132066 ), + new FieldElement( 24158887,12938817,11085297,-8177598,-28063478,-4457083,-30576463,64452,-6817084,-2692882 ), + new FieldElement( 13488534,7794716,22236231,5989356,25426474,-12578208,2350710,-3418511,-4688006,2364226 ) + ), + new GroupElementPreComp( + new FieldElement( 16335052,9132434,25640582,6678888,1725628,8517937,-11807024,-11697457,15445875,-7798101 ), + new FieldElement( 29004207,-7867081,28661402,-640412,-12794003,-7943086,31863255,-4135540,-278050,-15759279 ), + new FieldElement( -6122061,-14866665,-28614905,14569919,-10857999,-3591829,10343412,-6976290,-29828287,-10815811 ) + ), + new GroupElementPreComp( + new FieldElement( 27081650,3463984,14099042,-4517604,1616303,-6205604,29542636,15372179,17293797,960709 ), + new FieldElement( 20263915,11434237,-5765435,11236810,13505955,-10857102,-16111345,6493122,-19384511,7639714 ), + new FieldElement( -2830798,-14839232,25403038,-8215196,-8317012,-16173699,18006287,-16043750,29994677,-15808121 ) + ), + new GroupElementPreComp( + new FieldElement( 9769828,5202651,-24157398,-13631392,-28051003,-11561624,-24613141,-13860782,-31184575,709464 ), + new FieldElement( 12286395,13076066,-21775189,-1176622,-25003198,4057652,-32018128,-8890874,16102007,13205847 ), + new FieldElement( 13733362,5599946,10557076,3195751,-5557991,8536970,-25540170,8525972,10151379,10394400 ) + ), + new GroupElementPreComp( + new FieldElement( 4024660,-16137551,22436262,12276534,-9099015,-2686099,19698229,11743039,-33302334,8934414 ), + new FieldElement( -15879800,-4525240,-8580747,-2934061,14634845,-698278,-9449077,3137094,-11536886,11721158 ), + new FieldElement( 17555939,-5013938,8268606,2331751,-22738815,9761013,9319229,8835153,-9205489,-1280045 ) + ), + new GroupElementPreComp( + new FieldElement( -461409,-7830014,20614118,16688288,-7514766,-4807119,22300304,505429,6108462,-6183415 ), + new FieldElement( -5070281,12367917,-30663534,3234473,32617080,-8422642,29880583,-13483331,-26898490,-7867459 ), + new FieldElement( -31975283,5726539,26934134,10237677,-3173717,-605053,24199304,3795095,7592688,-14992079 ) + ), + new GroupElementPreComp( + new FieldElement( 21594432,-14964228,17466408,-4077222,32537084,2739898,6407723,12018833,-28256052,4298412 ), + new FieldElement( -20650503,-11961496,-27236275,570498,3767144,-1717540,13891942,-1569194,13717174,10805743 ), + new FieldElement( -14676630,-15644296,15287174,11927123,24177847,-8175568,-796431,14860609,-26938930,-5863836 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 12962541,5311799,-10060768,11658280,18855286,-7954201,13286263,-12808704,-4381056,9882022 ), + new FieldElement( 18512079,11319350,-20123124,15090309,18818594,5271736,-22727904,3666879,-23967430,-3299429 ), + new FieldElement( -6789020,-3146043,16192429,13241070,15898607,-14206114,-10084880,-6661110,-2403099,5276065 ) + ), + new GroupElementPreComp( + new FieldElement( 30169808,-5317648,26306206,-11750859,27814964,7069267,7152851,3684982,1449224,13082861 ), + new FieldElement( 10342826,3098505,2119311,193222,25702612,12233820,23697382,15056736,-21016438,-8202000 ), + new FieldElement( -33150110,3261608,22745853,7948688,19370557,-15177665,-26171976,6482814,-10300080,-11060101 ) + ), + new GroupElementPreComp( + new FieldElement( 32869458,-5408545,25609743,15678670,-10687769,-15471071,26112421,2521008,-22664288,6904815 ), + new FieldElement( 29506923,4457497,3377935,-9796444,-30510046,12935080,1561737,3841096,-29003639,-6657642 ), + new FieldElement( 10340844,-6630377,-18656632,-2278430,12621151,-13339055,30878497,-11824370,-25584551,5181966 ) + ), + new GroupElementPreComp( + new FieldElement( 25940115,-12658025,17324188,-10307374,-8671468,15029094,24396252,-16450922,-2322852,-12388574 ), + new FieldElement( -21765684,9916823,-1300409,4079498,-1028346,11909559,1782390,12641087,20603771,-6561742 ), + new FieldElement( -18882287,-11673380,24849422,11501709,13161720,-4768874,1925523,11914390,4662781,7820689 ) + ), + new GroupElementPreComp( + new FieldElement( 12241050,-425982,8132691,9393934,32846760,-1599620,29749456,12172924,16136752,15264020 ), + new FieldElement( -10349955,-14680563,-8211979,2330220,-17662549,-14545780,10658213,6671822,19012087,3772772 ), + new FieldElement( 3753511,-3421066,10617074,2028709,14841030,-6721664,28718732,-15762884,20527771,12988982 ) + ), + new GroupElementPreComp( + new FieldElement( -14822485,-5797269,-3707987,12689773,-898983,-10914866,-24183046,-10564943,3299665,-12424953 ), + new FieldElement( -16777703,-15253301,-9642417,4978983,3308785,8755439,6943197,6461331,-25583147,8991218 ), + new FieldElement( -17226263,1816362,-1673288,-6086439,31783888,-8175991,-32948145,7417950,-30242287,1507265 ) + ), + new GroupElementPreComp( + new FieldElement( 29692663,6829891,-10498800,4334896,20945975,-11906496,-28887608,8209391,14606362,-10647073 ), + new FieldElement( -3481570,8707081,32188102,5672294,22096700,1711240,-33020695,9761487,4170404,-2085325 ), + new FieldElement( -11587470,14855945,-4127778,-1531857,-26649089,15084046,22186522,16002000,-14276837,-8400798 ) + ), + new GroupElementPreComp( + new FieldElement( -4811456,13761029,-31703877,-2483919,-3312471,7869047,-7113572,-9620092,13240845,10965870 ), + new FieldElement( -7742563,-8256762,-14768334,-13656260,-23232383,12387166,4498947,14147411,29514390,4302863 ), + new FieldElement( -13413405,-12407859,20757302,-13801832,14785143,8976368,-5061276,-2144373,17846988,-13971927 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -2244452,-754728,-4597030,-1066309,-6247172,1455299,-21647728,-9214789,-5222701,12650267 ), + new FieldElement( -9906797,-16070310,21134160,12198166,-27064575,708126,387813,13770293,-19134326,10958663 ), + new FieldElement( 22470984,12369526,23446014,-5441109,-21520802,-9698723,-11772496,-11574455,-25083830,4271862 ) + ), + new GroupElementPreComp( + new FieldElement( -25169565,-10053642,-19909332,15361595,-5984358,2159192,75375,-4278529,-32526221,8469673 ), + new FieldElement( 15854970,4148314,-8893890,7259002,11666551,13824734,-30531198,2697372,24154791,-9460943 ), + new FieldElement( 15446137,-15806644,29759747,14019369,30811221,-9610191,-31582008,12840104,24913809,9815020 ) + ), + new GroupElementPreComp( + new FieldElement( -4709286,-5614269,-31841498,-12288893,-14443537,10799414,-9103676,13438769,18735128,9466238 ), + new FieldElement( 11933045,9281483,5081055,-5183824,-2628162,-4905629,-7727821,-10896103,-22728655,16199064 ), + new FieldElement( 14576810,379472,-26786533,-8317236,-29426508,-10812974,-102766,1876699,30801119,2164795 ) + ), + new GroupElementPreComp( + new FieldElement( 15995086,3199873,13672555,13712240,-19378835,-4647646,-13081610,-15496269,-13492807,1268052 ), + new FieldElement( -10290614,-3659039,-3286592,10948818,23037027,3794475,-3470338,-12600221,-17055369,3565904 ), + new FieldElement( 29210088,-9419337,-5919792,-4952785,10834811,-13327726,-16512102,-10820713,-27162222,-14030531 ) + ), + new GroupElementPreComp( + new FieldElement( -13161890,15508588,16663704,-8156150,-28349942,9019123,-29183421,-3769423,2244111,-14001979 ), + new FieldElement( -5152875,-3800936,-9306475,-6071583,16243069,14684434,-25673088,-16180800,13491506,4641841 ), + new FieldElement( 10813417,643330,-19188515,-728916,30292062,-16600078,27548447,-7721242,14476989,-12767431 ) + ), + new GroupElementPreComp( + new FieldElement( 10292079,9984945,6481436,8279905,-7251514,7032743,27282937,-1644259,-27912810,12651324 ), + new FieldElement( -31185513,-813383,22271204,11835308,10201545,15351028,17099662,3988035,21721536,-3148940 ), + new FieldElement( 10202177,-6545839,-31373232,-9574638,-32150642,-8119683,-12906320,3852694,13216206,14842320 ) + ), + new GroupElementPreComp( + new FieldElement( -15815640,-10601066,-6538952,-7258995,-6984659,-6581778,-31500847,13765824,-27434397,9900184 ), + new FieldElement( 14465505,-13833331,-32133984,-14738873,-27443187,12990492,33046193,15796406,-7051866,-8040114 ), + new FieldElement( 30924417,-8279620,6359016,-12816335,16508377,9071735,-25488601,15413635,9524356,-7018878 ) + ), + new GroupElementPreComp( + new FieldElement( 12274201,-13175547,32627641,-1785326,6736625,13267305,5237659,-5109483,15663516,4035784 ), + new FieldElement( -2951309,8903985,17349946,601635,-16432815,-4612556,-13732739,-15889334,-22258478,4659091 ), + new FieldElement( -16916263,-4952973,-30393711,-15158821,20774812,15897498,5736189,15026997,-2178256,-13455585 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -8858980,-2219056,28571666,-10155518,-474467,-10105698,-3801496,278095,23440562,-290208 ), + new FieldElement( 10226241,-5928702,15139956,120818,-14867693,5218603,32937275,11551483,-16571960,-7442864 ), + new FieldElement( 17932739,-12437276,-24039557,10749060,11316803,7535897,22503767,5561594,-3646624,3898661 ) + ), + new GroupElementPreComp( + new FieldElement( 7749907,-969567,-16339731,-16464,-25018111,15122143,-1573531,7152530,21831162,1245233 ), + new FieldElement( 26958459,-14658026,4314586,8346991,-5677764,11960072,-32589295,-620035,-30402091,-16716212 ), + new FieldElement( -12165896,9166947,33491384,13673479,29787085,13096535,6280834,14587357,-22338025,13987525 ) + ), + new GroupElementPreComp( + new FieldElement( -24349909,7778775,21116000,15572597,-4833266,-5357778,-4300898,-5124639,-7469781,-2858068 ), + new FieldElement( 9681908,-6737123,-31951644,13591838,-6883821,386950,31622781,6439245,-14581012,4091397 ), + new FieldElement( -8426427,1470727,-28109679,-1596990,3978627,-5123623,-19622683,12092163,29077877,-14741988 ) + ), + new GroupElementPreComp( + new FieldElement( 5269168,-6859726,-13230211,-8020715,25932563,1763552,-5606110,-5505881,-20017847,2357889 ), + new FieldElement( 32264008,-15407652,-5387735,-1160093,-2091322,-3946900,23104804,-12869908,5727338,189038 ), + new FieldElement( 14609123,-8954470,-6000566,-16622781,-14577387,-7743898,-26745169,10942115,-25888931,-14884697 ) + ), + new GroupElementPreComp( + new FieldElement( 20513500,5557931,-15604613,7829531,26413943,-2019404,-21378968,7471781,13913677,-5137875 ), + new FieldElement( -25574376,11967826,29233242,12948236,-6754465,4713227,-8940970,14059180,12878652,8511905 ), + new FieldElement( -25656801,3393631,-2955415,-7075526,-2250709,9366908,-30223418,6812974,5568676,-3127656 ) + ), + new GroupElementPreComp( + new FieldElement( 11630004,12144454,2116339,13606037,27378885,15676917,-17408753,-13504373,-14395196,8070818 ), + new FieldElement( 27117696,-10007378,-31282771,-5570088,1127282,12772488,-29845906,10483306,-11552749,-1028714 ), + new FieldElement( 10637467,-5688064,5674781,1072708,-26343588,-6982302,-1683975,9177853,-27493162,15431203 ) + ), + new GroupElementPreComp( + new FieldElement( 20525145,10892566,-12742472,12779443,-29493034,16150075,-28240519,14943142,-15056790,-7935931 ), + new FieldElement( -30024462,5626926,-551567,-9981087,753598,11981191,25244767,-3239766,-3356550,9594024 ), + new FieldElement( -23752644,2636870,-5163910,-10103818,585134,7877383,11345683,-6492290,13352335,-10977084 ) + ), + new GroupElementPreComp( + new FieldElement( -1931799,-5407458,3304649,-12884869,17015806,-4877091,-29783850,-7752482,-13215537,-319204 ), + new FieldElement( 20239939,6607058,6203985,3483793,-18386976,-779229,-20723742,15077870,-22750759,14523817 ), + new FieldElement( 27406042,-6041657,27423596,-4497394,4996214,10002360,-28842031,-4545494,-30172742,-4805667 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 11374242,12660715,17861383,-12540833,10935568,1099227,-13886076,-9091740,-27727044,11358504 ), + new FieldElement( -12730809,10311867,1510375,10778093,-2119455,-9145702,32676003,11149336,-26123651,4985768 ), + new FieldElement( -19096303,341147,-6197485,-239033,15756973,-8796662,-983043,13794114,-19414307,-15621255 ) + ), + new GroupElementPreComp( + new FieldElement( 6490081,11940286,25495923,-7726360,8668373,-8751316,3367603,6970005,-1691065,-9004790 ), + new FieldElement( 1656497,13457317,15370807,6364910,13605745,8362338,-19174622,-5475723,-16796596,-5031438 ), + new FieldElement( -22273315,-13524424,-64685,-4334223,-18605636,-10921968,-20571065,-7007978,-99853,-10237333 ) + ), + new GroupElementPreComp( + new FieldElement( 17747465,10039260,19368299,-4050591,-20630635,-16041286,31992683,-15857976,-29260363,-5511971 ), + new FieldElement( 31932027,-4986141,-19612382,16366580,22023614,88450,11371999,-3744247,4882242,-10626905 ), + new FieldElement( 29796507,37186,19818052,10115756,-11829032,3352736,18551198,3272828,-5190932,-4162409 ) + ), + new GroupElementPreComp( + new FieldElement( 12501286,4044383,-8612957,-13392385,-32430052,5136599,-19230378,-3529697,330070,-3659409 ), + new FieldElement( 6384877,2899513,17807477,7663917,-2358888,12363165,25366522,-8573892,-271295,12071499 ), + new FieldElement( -8365515,-4042521,25133448,-4517355,-6211027,2265927,-32769618,1936675,-5159697,3829363 ) + ), + new GroupElementPreComp( + new FieldElement( 28425966,-5835433,-577090,-4697198,-14217555,6870930,7921550,-6567787,26333140,14267664 ), + new FieldElement( -11067219,11871231,27385719,-10559544,-4585914,-11189312,10004786,-8709488,-21761224,8930324 ), + new FieldElement( -21197785,-16396035,25654216,-1725397,12282012,11008919,1541940,4757911,-26491501,-16408940 ) + ), + new GroupElementPreComp( + new FieldElement( 13537262,-7759490,-20604840,10961927,-5922820,-13218065,-13156584,6217254,-15943699,13814990 ), + new FieldElement( -17422573,15157790,18705543,29619,24409717,-260476,27361681,9257833,-1956526,-1776914 ), + new FieldElement( -25045300,-10191966,15366585,15166509,-13105086,8423556,-29171540,12361135,-18685978,4578290 ) + ), + new GroupElementPreComp( + new FieldElement( 24579768,3711570,1342322,-11180126,-27005135,14124956,-22544529,14074919,21964432,8235257 ), + new FieldElement( -6528613,-2411497,9442966,-5925588,12025640,-1487420,-2981514,-1669206,13006806,2355433 ), + new FieldElement( -16304899,-13605259,-6632427,-5142349,16974359,-10911083,27202044,1719366,1141648,-12796236 ) + ), + new GroupElementPreComp( + new FieldElement( -12863944,-13219986,-8318266,-11018091,-6810145,-4843894,13475066,-3133972,32674895,13715045 ), + new FieldElement( 11423335,-5468059,32344216,8962751,24989809,9241752,-13265253,16086212,-28740881,-15642093 ), + new FieldElement( -1409668,12530728,-6368726,10847387,19531186,-14132160,-11709148,7791794,-27245943,4383347 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -28970898,5271447,-1266009,-9736989,-12455236,16732599,-4862407,-4906449,27193557,6245191 ), + new FieldElement( -15193956,5362278,-1783893,2695834,4960227,12840725,23061898,3260492,22510453,8577507 ), + new FieldElement( -12632451,11257346,-32692994,13548177,-721004,10879011,31168030,13952092,-29571492,-3635906 ) + ), + new GroupElementPreComp( + new FieldElement( 3877321,-9572739,32416692,5405324,-11004407,-13656635,3759769,11935320,5611860,8164018 ), + new FieldElement( -16275802,14667797,15906460,12155291,-22111149,-9039718,32003002,-8832289,5773085,-8422109 ), + new FieldElement( -23788118,-8254300,1950875,8937633,18686727,16459170,-905725,12376320,31632953,190926 ) + ), + new GroupElementPreComp( + new FieldElement( -24593607,-16138885,-8423991,13378746,14162407,6901328,-8288749,4508564,-25341555,-3627528 ), + new FieldElement( 8884438,-5884009,6023974,10104341,-6881569,-4941533,18722941,-14786005,-1672488,827625 ), + new FieldElement( -32720583,-16289296,-32503547,7101210,13354605,2659080,-1800575,-14108036,-24878478,1541286 ) + ), + new GroupElementPreComp( + new FieldElement( 2901347,-1117687,3880376,-10059388,-17620940,-3612781,-21802117,-3567481,20456845,-1885033 ), + new FieldElement( 27019610,12299467,-13658288,-1603234,-12861660,-4861471,-19540150,-5016058,29439641,15138866 ), + new FieldElement( 21536104,-6626420,-32447818,-10690208,-22408077,5175814,-5420040,-16361163,7779328,109896 ) + ), + new GroupElementPreComp( + new FieldElement( 30279744,14648750,-8044871,6425558,13639621,-743509,28698390,12180118,23177719,-554075 ), + new FieldElement( 26572847,3405927,-31701700,12890905,-19265668,5335866,-6493768,2378492,4439158,-13279347 ), + new FieldElement( -22716706,3489070,-9225266,-332753,18875722,-1140095,14819434,-12731527,-17717757,-5461437 ) + ), + new GroupElementPreComp( + new FieldElement( -5056483,16566551,15953661,3767752,-10436499,15627060,-820954,2177225,8550082,-15114165 ), + new FieldElement( -18473302,16596775,-381660,15663611,22860960,15585581,-27844109,-3582739,-23260460,-8428588 ), + new FieldElement( -32480551,15707275,-8205912,-5652081,29464558,2713815,-22725137,15860482,-21902570,1494193 ) + ), + new GroupElementPreComp( + new FieldElement( -19562091,-14087393,-25583872,-9299552,13127842,759709,21923482,16529112,8742704,12967017 ), + new FieldElement( -28464899,1553205,32536856,-10473729,-24691605,-406174,-8914625,-2933896,-29903758,15553883 ), + new FieldElement( 21877909,3230008,9881174,10539357,-4797115,2841332,11543572,14513274,19375923,-12647961 ) + ), + new GroupElementPreComp( + new FieldElement( 8832269,-14495485,13253511,5137575,5037871,4078777,24880818,-6222716,2862653,9455043 ), + new FieldElement( 29306751,5123106,20245049,-14149889,9592566,8447059,-2077124,-2990080,15511449,4789663 ), + new FieldElement( -20679756,7004547,8824831,-9434977,-4045704,-3750736,-5754762,108893,23513200,16652362 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -33256173,4144782,-4476029,-6579123,10770039,-7155542,-6650416,-12936300,-18319198,10212860 ), + new FieldElement( 2756081,8598110,7383731,-6859892,22312759,-1105012,21179801,2600940,-9988298,-12506466 ), + new FieldElement( -24645692,13317462,-30449259,-15653928,21365574,-10869657,11344424,864440,-2499677,-16710063 ) + ), + new GroupElementPreComp( + new FieldElement( -26432803,6148329,-17184412,-14474154,18782929,-275997,-22561534,211300,2719757,4940997 ), + new FieldElement( -1323882,3911313,-6948744,14759765,-30027150,7851207,21690126,8518463,26699843,5276295 ), + new FieldElement( -13149873,-6429067,9396249,365013,24703301,-10488939,1321586,149635,-15452774,7159369 ) + ), + new GroupElementPreComp( + new FieldElement( 9987780,-3404759,17507962,9505530,9731535,-2165514,22356009,8312176,22477218,-8403385 ), + new FieldElement( 18155857,-16504990,19744716,9006923,15154154,-10538976,24256460,-4864995,-22548173,9334109 ), + new FieldElement( 2986088,-4911893,10776628,-3473844,10620590,-7083203,-21413845,14253545,-22587149,536906 ) + ), + new GroupElementPreComp( + new FieldElement( 4377756,8115836,24567078,15495314,11625074,13064599,7390551,10589625,10838060,-15420424 ), + new FieldElement( -19342404,867880,9277171,-3218459,-14431572,-1986443,19295826,-15796950,6378260,699185 ), + new FieldElement( 7895026,4057113,-7081772,-13077756,-17886831,-323126,-716039,15693155,-5045064,-13373962 ) + ), + new GroupElementPreComp( + new FieldElement( -7737563,-5869402,-14566319,-7406919,11385654,13201616,31730678,-10962840,-3918636,-9669325 ), + new FieldElement( 10188286,-15770834,-7336361,13427543,22223443,14896287,30743455,7116568,-21786507,5427593 ), + new FieldElement( 696102,13206899,27047647,-10632082,15285305,-9853179,10798490,-4578720,19236243,12477404 ) + ), + new GroupElementPreComp( + new FieldElement( -11229439,11243796,-17054270,-8040865,-788228,-8167967,-3897669,11180504,-23169516,7733644 ), + new FieldElement( 17800790,-14036179,-27000429,-11766671,23887827,3149671,23466177,-10538171,10322027,15313801 ), + new FieldElement( 26246234,11968874,32263343,-5468728,6830755,-13323031,-15794704,-101982,-24449242,10890804 ) + ), + new GroupElementPreComp( + new FieldElement( -31365647,10271363,-12660625,-6267268,16690207,-13062544,-14982212,16484931,25180797,-5334884 ), + new FieldElement( -586574,10376444,-32586414,-11286356,19801893,10997610,2276632,9482883,316878,13820577 ), + new FieldElement( -9882808,-4510367,-2115506,16457136,-11100081,11674996,30756178,-7515054,30696930,-3712849 ) + ), + new GroupElementPreComp( + new FieldElement( 32988917,-9603412,12499366,7910787,-10617257,-11931514,-7342816,-9985397,-32349517,7392473 ), + new FieldElement( -8855661,15927861,9866406,-3649411,-2396914,-16655781,-30409476,-9134995,25112947,-2926644 ), + new FieldElement( -2504044,-436966,25621774,-5678772,15085042,-5479877,-24884878,-13526194,5537438,-13914319 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -11225584,2320285,-9584280,10149187,-33444663,5808648,-14876251,-1729667,31234590,6090599 ), + new FieldElement( -9633316,116426,26083934,2897444,-6364437,-2688086,609721,15878753,-6970405,-9034768 ), + new FieldElement( -27757857,247744,-15194774,-9002551,23288161,-10011936,-23869595,6503646,20650474,1804084 ) + ), + new GroupElementPreComp( + new FieldElement( -27589786,15456424,8972517,8469608,15640622,4439847,3121995,-10329713,27842616,-202328 ), + new FieldElement( -15306973,2839644,22530074,10026331,4602058,5048462,28248656,5031932,-11375082,12714369 ), + new FieldElement( 20807691,-7270825,29286141,11421711,-27876523,-13868230,-21227475,1035546,-19733229,12796920 ) + ), + new GroupElementPreComp( + new FieldElement( 12076899,-14301286,-8785001,-11848922,-25012791,16400684,-17591495,-12899438,3480665,-15182815 ), + new FieldElement( -32361549,5457597,28548107,7833186,7303070,-11953545,-24363064,-15921875,-33374054,2771025 ), + new FieldElement( -21389266,421932,26597266,6860826,22486084,-6737172,-17137485,-4210226,-24552282,15673397 ) + ), + new GroupElementPreComp( + new FieldElement( -20184622,2338216,19788685,-9620956,-4001265,-8740893,-20271184,4733254,3727144,-12934448 ), + new FieldElement( 6120119,814863,-11794402,-622716,6812205,-15747771,2019594,7975683,31123697,-10958981 ), + new FieldElement( 30069250,-11435332,30434654,2958439,18399564,-976289,12296869,9204260,-16432438,9648165 ) + ), + new GroupElementPreComp( + new FieldElement( 32705432,-1550977,30705658,7451065,-11805606,9631813,3305266,5248604,-26008332,-11377501 ), + new FieldElement( 17219865,2375039,-31570947,-5575615,-19459679,9219903,294711,15298639,2662509,-16297073 ), + new FieldElement( -1172927,-7558695,-4366770,-4287744,-21346413,-8434326,32087529,-1222777,32247248,-14389861 ) + ), + new GroupElementPreComp( + new FieldElement( 14312628,1221556,17395390,-8700143,-4945741,-8684635,-28197744,-9637817,-16027623,-13378845 ), + new FieldElement( -1428825,-9678990,-9235681,6549687,-7383069,-468664,23046502,9803137,17597934,2346211 ), + new FieldElement( 18510800,15337574,26171504,981392,-22241552,7827556,-23491134,-11323352,3059833,-11782870 ) + ), + new GroupElementPreComp( + new FieldElement( 10141598,6082907,17829293,-1947643,9830092,13613136,-25556636,-5544586,-33502212,3592096 ), + new FieldElement( 33114168,-15889352,-26525686,-13343397,33076705,8716171,1151462,1521897,-982665,-6837803 ), + new FieldElement( -32939165,-4255815,23947181,-324178,-33072974,-12305637,-16637686,3891704,26353178,693168 ) + ), + new GroupElementPreComp( + new FieldElement( 30374239,1595580,-16884039,13186931,4600344,406904,9585294,-400668,31375464,14369965 ), + new FieldElement( -14370654,-7772529,1510301,6434173,-18784789,-6262728,32732230,-13108839,17901441,16011505 ), + new FieldElement( 18171223,-11934626,-12500402,15197122,-11038147,-15230035,-19172240,-16046376,8764035,12309598 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 5975908,-5243188,-19459362,-9681747,-11541277,14015782,-23665757,1228319,17544096,-10593782 ), + new FieldElement( 5811932,-1715293,3442887,-2269310,-18367348,-8359541,-18044043,-15410127,-5565381,12348900 ), + new FieldElement( -31399660,11407555,25755363,6891399,-3256938,14872274,-24849353,8141295,-10632534,-585479 ) + ), + new GroupElementPreComp( + new FieldElement( -12675304,694026,-5076145,13300344,14015258,-14451394,-9698672,-11329050,30944593,1130208 ), + new FieldElement( 8247766,-6710942,-26562381,-7709309,-14401939,-14648910,4652152,2488540,23550156,-271232 ), + new FieldElement( 17294316,-3788438,7026748,15626851,22990044,113481,2267737,-5908146,-408818,-137719 ) + ), + new GroupElementPreComp( + new FieldElement( 16091085,-16253926,18599252,7340678,2137637,-1221657,-3364161,14550936,3260525,-7166271 ), + new FieldElement( -4910104,-13332887,18550887,10864893,-16459325,-7291596,-23028869,-13204905,-12748722,2701326 ), + new FieldElement( -8574695,16099415,4629974,-16340524,-20786213,-6005432,-10018363,9276971,11329923,1862132 ) + ), + new GroupElementPreComp( + new FieldElement( 14763076,-15903608,-30918270,3689867,3511892,10313526,-21951088,12219231,-9037963,-940300 ), + new FieldElement( 8894987,-3446094,6150753,3013931,301220,15693451,-31981216,-2909717,-15438168,11595570 ), + new FieldElement( 15214962,3537601,-26238722,-14058872,4418657,-15230761,13947276,10730794,-13489462,-4363670 ) + ), + new GroupElementPreComp( + new FieldElement( -2538306,7682793,32759013,263109,-29984731,-7955452,-22332124,-10188635,977108,699994 ), + new FieldElement( -12466472,4195084,-9211532,550904,-15565337,12917920,19118110,-439841,-30534533,-14337913 ), + new FieldElement( 31788461,-14507657,4799989,7372237,8808585,-14747943,9408237,-10051775,12493932,-5409317 ) + ), + new GroupElementPreComp( + new FieldElement( -25680606,5260744,-19235809,-6284470,-3695942,16566087,27218280,2607121,29375955,6024730 ), + new FieldElement( 842132,-2794693,-4763381,-8722815,26332018,-12405641,11831880,6985184,-9940361,2854096 ), + new FieldElement( -4847262,-7969331,2516242,-5847713,9695691,-7221186,16512645,960770,12121869,16648078 ) + ), + new GroupElementPreComp( + new FieldElement( -15218652,14667096,-13336229,2013717,30598287,-464137,-31504922,-7882064,20237806,2838411 ), + new FieldElement( -19288047,4453152,15298546,-16178388,22115043,-15972604,12544294,-13470457,1068881,-12499905 ), + new FieldElement( -9558883,-16518835,33238498,13506958,30505848,-1114596,-8486907,-2630053,12521378,4845654 ) + ), + new GroupElementPreComp( + new FieldElement( -28198521,10744108,-2958380,10199664,7759311,-13088600,3409348,-873400,-6482306,-12885870 ), + new FieldElement( -23561822,6230156,-20382013,10655314,-24040585,-11621172,10477734,-1240216,-3113227,13974498 ), + new FieldElement( 12966261,15550616,-32038948,-1615346,21025980,-629444,5642325,7188737,18895762,12629579 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 14741879,-14946887,22177208,-11721237,1279741,8058600,11758140,789443,32195181,3895677 ), + new FieldElement( 10758205,15755439,-4509950,9243698,-4879422,6879879,-2204575,-3566119,-8982069,4429647 ), + new FieldElement( -2453894,15725973,-20436342,-10410672,-5803908,-11040220,-7135870,-11642895,18047436,-15281743 ) + ), + new GroupElementPreComp( + new FieldElement( -25173001,-11307165,29759956,11776784,-22262383,-15820455,10993114,-12850837,-17620701,-9408468 ), + new FieldElement( 21987233,700364,-24505048,14972008,-7774265,-5718395,32155026,2581431,-29958985,8773375 ), + new FieldElement( -25568350,454463,-13211935,16126715,25240068,8594567,20656846,12017935,-7874389,-13920155 ) + ), + new GroupElementPreComp( + new FieldElement( 6028182,6263078,-31011806,-11301710,-818919,2461772,-31841174,-5468042,-1721788,-2776725 ), + new FieldElement( -12278994,16624277,987579,-5922598,32908203,1248608,7719845,-4166698,28408820,6816612 ), + new FieldElement( -10358094,-8237829,19549651,-12169222,22082623,16147817,20613181,13982702,-10339570,5067943 ) + ), + new GroupElementPreComp( + new FieldElement( -30505967,-3821767,12074681,13582412,-19877972,2443951,-19719286,12746132,5331210,-10105944 ), + new FieldElement( 30528811,3601899,-1957090,4619785,-27361822,-15436388,24180793,-12570394,27679908,-1648928 ), + new FieldElement( 9402404,-13957065,32834043,10838634,-26580150,-13237195,26653274,-8685565,22611444,-12715406 ) + ), + new GroupElementPreComp( + new FieldElement( 22190590,1118029,22736441,15130463,-30460692,-5991321,19189625,-4648942,4854859,6622139 ), + new FieldElement( -8310738,-2953450,-8262579,-3388049,-10401731,-271929,13424426,-3567227,26404409,13001963 ), + new FieldElement( -31241838,-15415700,-2994250,8939346,11562230,-12840670,-26064365,-11621720,-15405155,11020693 ) + ), + new GroupElementPreComp( + new FieldElement( 1866042,-7949489,-7898649,-10301010,12483315,13477547,3175636,-12424163,28761762,1406734 ), + new FieldElement( -448555,-1777666,13018551,3194501,-9580420,-11161737,24760585,-4347088,25577411,-13378680 ), + new FieldElement( -24290378,4759345,-690653,-1852816,2066747,10693769,-29595790,9884936,-9368926,4745410 ) + ), + new GroupElementPreComp( + new FieldElement( -9141284,6049714,-19531061,-4341411,-31260798,9944276,-15462008,-11311852,10931924,-11931931 ), + new FieldElement( -16561513,14112680,-8012645,4817318,-8040464,-11414606,-22853429,10856641,-20470770,13434654 ), + new FieldElement( 22759489,-10073434,-16766264,-1871422,13637442,-10168091,1765144,-12654326,28445307,-5364710 ) + ), + new GroupElementPreComp( + new FieldElement( 29875063,12493613,2795536,-3786330,1710620,15181182,-10195717,-8788675,9074234,1167180 ), + new FieldElement( -26205683,11014233,-9842651,-2635485,-26908120,7532294,-18716888,-9535498,3843903,9367684 ), + new FieldElement( -10969595,-6403711,9591134,9582310,11349256,108879,16235123,8601684,-139197,4242895 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 22092954,-13191123,-2042793,-11968512,32186753,-11517388,-6574341,2470660,-27417366,16625501 ), + new FieldElement( -11057722,3042016,13770083,-9257922,584236,-544855,-7770857,2602725,-27351616,14247413 ), + new FieldElement( 6314175,-10264892,-32772502,15957557,-10157730,168750,-8618807,14290061,27108877,-1180880 ) + ), + new GroupElementPreComp( + new FieldElement( -8586597,-7170966,13241782,10960156,-32991015,-13794596,33547976,-11058889,-27148451,981874 ), + new FieldElement( 22833440,9293594,-32649448,-13618667,-9136966,14756819,-22928859,-13970780,-10479804,-16197962 ), + new FieldElement( -7768587,3326786,-28111797,10783824,19178761,14905060,22680049,13906969,-15933690,3797899 ) + ), + new GroupElementPreComp( + new FieldElement( 21721356,-4212746,-12206123,9310182,-3882239,-13653110,23740224,-2709232,20491983,-8042152 ), + new FieldElement( 9209270,-15135055,-13256557,-6167798,-731016,15289673,25947805,15286587,30997318,-6703063 ), + new FieldElement( 7392032,16618386,23946583,-8039892,-13265164,-1533858,-14197445,-2321576,17649998,-250080 ) + ), + new GroupElementPreComp( + new FieldElement( -9301088,-14193827,30609526,-3049543,-25175069,-1283752,-15241566,-9525724,-2233253,7662146 ), + new FieldElement( -17558673,1763594,-33114336,15908610,-30040870,-12174295,7335080,-8472199,-3174674,3440183 ), + new FieldElement( -19889700,-5977008,-24111293,-9688870,10799743,-16571957,40450,-4431835,4862400,1133 ) + ), + new GroupElementPreComp( + new FieldElement( -32856209,-7873957,-5422389,14860950,-16319031,7956142,7258061,311861,-30594991,-7379421 ), + new FieldElement( -3773428,-1565936,28985340,7499440,24445838,9325937,29727763,16527196,18278453,15405622 ), + new FieldElement( -4381906,8508652,-19898366,-3674424,-5984453,15149970,-13313598,843523,-21875062,13626197 ) + ), + new GroupElementPreComp( + new FieldElement( 2281448,-13487055,-10915418,-2609910,1879358,16164207,-10783882,3953792,13340839,15928663 ), + new FieldElement( 31727126,-7179855,-18437503,-8283652,2875793,-16390330,-25269894,-7014826,-23452306,5964753 ), + new FieldElement( 4100420,-5959452,-17179337,6017714,-18705837,12227141,-26684835,11344144,2538215,-7570755 ) + ), + new GroupElementPreComp( + new FieldElement( -9433605,6123113,11159803,-2156608,30016280,14966241,-20474983,1485421,-629256,-15958862 ), + new FieldElement( -26804558,4260919,11851389,9658551,-32017107,16367492,-20205425,-13191288,11659922,-11115118 ), + new FieldElement( 26180396,10015009,-30844224,-8581293,5418197,9480663,2231568,-10170080,33100372,-1306171 ) + ), + new GroupElementPreComp( + new FieldElement( 15121113,-5201871,-10389905,15427821,-27509937,-15992507,21670947,4486675,-5931810,-14466380 ), + new FieldElement( 16166486,-9483733,-11104130,6023908,-31926798,-1364923,2340060,-16254968,-10735770,-10039824 ), + new FieldElement( 28042865,-3557089,-12126526,12259706,-3717498,-6945899,6766453,-8689599,18036436,5803270 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -817581,6763912,11803561,1585585,10958447,-2671165,23855391,4598332,-6159431,-14117438 ), + new FieldElement( -31031306,-14256194,17332029,-2383520,31312682,-5967183,696309,50292,-20095739,11763584 ), + new FieldElement( -594563,-2514283,-32234153,12643980,12650761,14811489,665117,-12613632,-19773211,-10713562 ) + ), + new GroupElementPreComp( + new FieldElement( 30464590,-11262872,-4127476,-12734478,19835327,-7105613,-24396175,2075773,-17020157,992471 ), + new FieldElement( 18357185,-6994433,7766382,16342475,-29324918,411174,14578841,8080033,-11574335,-10601610 ), + new FieldElement( 19598397,10334610,12555054,2555664,18821899,-10339780,21873263,16014234,26224780,16452269 ) + ), + new GroupElementPreComp( + new FieldElement( -30223925,5145196,5944548,16385966,3976735,2009897,-11377804,-7618186,-20533829,3698650 ), + new FieldElement( 14187449,3448569,-10636236,-10810935,-22663880,-3433596,7268410,-10890444,27394301,12015369 ), + new FieldElement( 19695761,16087646,28032085,12999827,6817792,11427614,20244189,-1312777,-13259127,-3402461 ) + ), + new GroupElementPreComp( + new FieldElement( 30860103,12735208,-1888245,-4699734,-16974906,2256940,-8166013,12298312,-8550524,-10393462 ), + new FieldElement( -5719826,-11245325,-1910649,15569035,26642876,-7587760,-5789354,-15118654,-4976164,12651793 ), + new FieldElement( -2848395,9953421,11531313,-5282879,26895123,-12697089,-13118820,-16517902,9768698,-2533218 ) + ), + new GroupElementPreComp( + new FieldElement( -24719459,1894651,-287698,-4704085,15348719,-8156530,32767513,12765450,4940095,10678226 ), + new FieldElement( 18860224,15980149,-18987240,-1562570,-26233012,-11071856,-7843882,13944024,-24372348,16582019 ), + new FieldElement( -15504260,4970268,-29893044,4175593,-20993212,-2199756,-11704054,15444560,-11003761,7989037 ) + ), + new GroupElementPreComp( + new FieldElement( 31490452,5568061,-2412803,2182383,-32336847,4531686,-32078269,6200206,-19686113,-14800171 ), + new FieldElement( -17308668,-15879940,-31522777,-2831,-32887382,16375549,8680158,-16371713,28550068,-6857132 ), + new FieldElement( -28126887,-5688091,16837845,-1820458,-6850681,12700016,-30039981,4364038,1155602,5988841 ) + ), + new GroupElementPreComp( + new FieldElement( 21890435,-13272907,-12624011,12154349,-7831873,15300496,23148983,-4470481,24618407,8283181 ), + new FieldElement( -33136107,-10512751,9975416,6841041,-31559793,16356536,3070187,-7025928,1466169,10740210 ), + new FieldElement( -1509399,-15488185,-13503385,-10655916,32799044,909394,-13938903,-5779719,-32164649,-15327040 ) + ), + new GroupElementPreComp( + new FieldElement( 3960823,-14267803,-28026090,-15918051,-19404858,13146868,15567327,951507,-3260321,-573935 ), + new FieldElement( 24740841,5052253,-30094131,8961361,25877428,6165135,-24368180,14397372,-7380369,-6144105 ), + new FieldElement( -28888365,3510803,-28103278,-1158478,-11238128,-10631454,-15441463,-14453128,-1625486,-6494814 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 793299,-9230478,8836302,-6235707,-27360908,-2369593,33152843,-4885251,-9906200,-621852 ), + new FieldElement( 5666233,525582,20782575,-8038419,-24538499,14657740,16099374,1468826,-6171428,-15186581 ), + new FieldElement( -4859255,-3779343,-2917758,-6748019,7778750,11688288,-30404353,-9871238,-1558923,-9863646 ) + ), + new GroupElementPreComp( + new FieldElement( 10896332,-7719704,824275,472601,-19460308,3009587,25248958,14783338,-30581476,-15757844 ), + new FieldElement( 10566929,12612572,-31944212,11118703,-12633376,12362879,21752402,8822496,24003793,14264025 ), + new FieldElement( 27713862,-7355973,-11008240,9227530,27050101,2504721,23886875,-13117525,13958495,-5732453 ) + ), + new GroupElementPreComp( + new FieldElement( -23481610,4867226,-27247128,3900521,29838369,-8212291,-31889399,-10041781,7340521,-15410068 ), + new FieldElement( 4646514,-8011124,-22766023,-11532654,23184553,8566613,31366726,-1381061,-15066784,-10375192 ), + new FieldElement( -17270517,12723032,-16993061,14878794,21619651,-6197576,27584817,3093888,-8843694,3849921 ) + ), + new GroupElementPreComp( + new FieldElement( -9064912,2103172,25561640,-15125738,-5239824,9582958,32477045,-9017955,5002294,-15550259 ), + new FieldElement( -12057553,-11177906,21115585,-13365155,8808712,-12030708,16489530,13378448,-25845716,12741426 ), + new FieldElement( -5946367,10645103,-30911586,15390284,-3286982,-7118677,24306472,15852464,28834118,-7646072 ) + ), + new GroupElementPreComp( + new FieldElement( -17335748,-9107057,-24531279,9434953,-8472084,-583362,-13090771,455841,20461858,5491305 ), + new FieldElement( 13669248,-16095482,-12481974,-10203039,-14569770,-11893198,-24995986,11293807,-28588204,-9421832 ), + new FieldElement( 28497928,6272777,-33022994,14470570,8906179,-1225630,18504674,-14165166,29867745,-8795943 ) + ), + new GroupElementPreComp( + new FieldElement( -16207023,13517196,-27799630,-13697798,24009064,-6373891,-6367600,-13175392,22853429,-4012011 ), + new FieldElement( 24191378,16712145,-13931797,15217831,14542237,1646131,18603514,-11037887,12876623,-2112447 ), + new FieldElement( 17902668,4518229,-411702,-2829247,26878217,5258055,-12860753,608397,16031844,3723494 ) + ), + new GroupElementPreComp( + new FieldElement( -28632773,12763728,-20446446,7577504,33001348,-13017745,17558842,-7872890,23896954,-4314245 ), + new FieldElement( -20005381,-12011952,31520464,605201,2543521,5991821,-2945064,7229064,-9919646,-8826859 ), + new FieldElement( 28816045,298879,-28165016,-15920938,19000928,-1665890,-12680833,-2949325,-18051778,-2082915 ) + ), + new GroupElementPreComp( + new FieldElement( 16000882,-344896,3493092,-11447198,-29504595,-13159789,12577740,16041268,-19715240,7847707 ), + new FieldElement( 10151868,10572098,27312476,7922682,14825339,4723128,-32855931,-6519018,-10020567,3852848 ), + new FieldElement( -11430470,15697596,-21121557,-4420647,5386314,15063598,16514493,-15932110,29330899,-15076224 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -25499735,-4378794,-15222908,-6901211,16615731,2051784,3303702,15490,-27548796,12314391 ), + new FieldElement( 15683520,-6003043,18109120,-9980648,15337968,-5997823,-16717435,15921866,16103996,-3731215 ), + new FieldElement( -23169824,-10781249,13588192,-1628807,-3798557,-1074929,-19273607,5402699,-29815713,-9841101 ) + ), + new GroupElementPreComp( + new FieldElement( 23190676,2384583,-32714340,3462154,-29903655,-1529132,-11266856,8911517,-25205859,2739713 ), + new FieldElement( 21374101,-3554250,-33524649,9874411,15377179,11831242,-33529904,6134907,4931255,11987849 ), + new FieldElement( -7732,-2978858,-16223486,7277597,105524,-322051,-31480539,13861388,-30076310,10117930 ) + ), + new GroupElementPreComp( + new FieldElement( -29501170,-10744872,-26163768,13051539,-25625564,5089643,-6325503,6704079,12890019,15728940 ), + new FieldElement( -21972360,-11771379,-951059,-4418840,14704840,2695116,903376,-10428139,12885167,8311031 ), + new FieldElement( -17516482,5352194,10384213,-13811658,7506451,13453191,26423267,4384730,1888765,-5435404 ) + ), + new GroupElementPreComp( + new FieldElement( -25817338,-3107312,-13494599,-3182506,30896459,-13921729,-32251644,-12707869,-19464434,-3340243 ), + new FieldElement( -23607977,-2665774,-526091,4651136,5765089,4618330,6092245,14845197,17151279,-9854116 ), + new FieldElement( -24830458,-12733720,-15165978,10367250,-29530908,-265356,22825805,-7087279,-16866484,16176525 ) + ), + new GroupElementPreComp( + new FieldElement( -23583256,6564961,20063689,3798228,-4740178,7359225,2006182,-10363426,-28746253,-10197509 ), + new FieldElement( -10626600,-4486402,-13320562,-5125317,3432136,-6393229,23632037,-1940610,32808310,1099883 ), + new FieldElement( 15030977,5768825,-27451236,-2887299,-6427378,-15361371,-15277896,-6809350,2051441,-15225865 ) + ), + new GroupElementPreComp( + new FieldElement( -3362323,-7239372,7517890,9824992,23555850,295369,5148398,-14154188,-22686354,16633660 ), + new FieldElement( 4577086,-16752288,13249841,-15304328,19958763,-14537274,18559670,-10759549,8402478,-9864273 ), + new FieldElement( -28406330,-1051581,-26790155,-907698,-17212414,-11030789,9453451,-14980072,17983010,9967138 ) + ), + new GroupElementPreComp( + new FieldElement( -25762494,6524722,26585488,9969270,24709298,1220360,-1677990,7806337,17507396,3651560 ), + new FieldElement( -10420457,-4118111,14584639,15971087,-15768321,8861010,26556809,-5574557,-18553322,-11357135 ), + new FieldElement( 2839101,14284142,4029895,3472686,14402957,12689363,-26642121,8459447,-5605463,-7621941 ) + ), + new GroupElementPreComp( + new FieldElement( -4839289,-3535444,9744961,2871048,25113978,3187018,-25110813,-849066,17258084,-7977739 ), + new FieldElement( 18164541,-10595176,-17154882,-1542417,19237078,-9745295,23357533,-15217008,26908270,12150756 ), + new FieldElement( -30264870,-7647865,5112249,-7036672,-1499807,-6974257,43168,-5537701,-32302074,16215819 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -6898905,9824394,-12304779,-4401089,-31397141,-6276835,32574489,12532905,-7503072,-8675347 ), + new FieldElement( -27343522,-16515468,-27151524,-10722951,946346,16291093,254968,7168080,21676107,-1943028 ), + new FieldElement( 21260961,-8424752,-16831886,-11920822,-23677961,3968121,-3651949,-6215466,-3556191,-7913075 ) + ), + new GroupElementPreComp( + new FieldElement( 16544754,13250366,-16804428,15546242,-4583003,12757258,-2462308,-8680336,-18907032,-9662799 ), + new FieldElement( -2415239,-15577728,18312303,4964443,-15272530,-12653564,26820651,16690659,25459437,-4564609 ), + new FieldElement( -25144690,11425020,28423002,-11020557,-6144921,-15826224,9142795,-2391602,-6432418,-1644817 ) + ), + new GroupElementPreComp( + new FieldElement( -23104652,6253476,16964147,-3768872,-25113972,-12296437,-27457225,-16344658,6335692,7249989 ), + new FieldElement( -30333227,13979675,7503222,-12368314,-11956721,-4621693,-30272269,2682242,25993170,-12478523 ), + new FieldElement( 4364628,5930691,32304656,-10044554,-8054781,15091131,22857016,-10598955,31820368,15075278 ) + ), + new GroupElementPreComp( + new FieldElement( 31879134,-8918693,17258761,90626,-8041836,-4917709,24162788,-9650886,-17970238,12833045 ), + new FieldElement( 19073683,14851414,-24403169,-11860168,7625278,11091125,-19619190,2074449,-9413939,14905377 ), + new FieldElement( 24483667,-11935567,-2518866,-11547418,-1553130,15355506,-25282080,9253129,27628530,-7555480 ) + ), + new GroupElementPreComp( + new FieldElement( 17597607,8340603,19355617,552187,26198470,-3176583,4593324,-9157582,-14110875,15297016 ), + new FieldElement( 510886,14337390,-31785257,16638632,6328095,2713355,-20217417,-11864220,8683221,2921426 ), + new FieldElement( 18606791,11874196,27155355,-5281482,-24031742,6265446,-25178240,-1278924,4674690,13890525 ) + ), + new GroupElementPreComp( + new FieldElement( 13609624,13069022,-27372361,-13055908,24360586,9592974,14977157,9835105,4389687,288396 ), + new FieldElement( 9922506,-519394,13613107,5883594,-18758345,-434263,-12304062,8317628,23388070,16052080 ), + new FieldElement( 12720016,11937594,-31970060,-5028689,26900120,8561328,-20155687,-11632979,-14754271,-10812892 ) + ), + new GroupElementPreComp( + new FieldElement( 15961858,14150409,26716931,-665832,-22794328,13603569,11829573,7467844,-28822128,929275 ), + new FieldElement( 11038231,-11582396,-27310482,-7316562,-10498527,-16307831,-23479533,-9371869,-21393143,2465074 ), + new FieldElement( 20017163,-4323226,27915242,1529148,12396362,15675764,13817261,-9658066,2463391,-4622140 ) + ), + new GroupElementPreComp( + new FieldElement( -16358878,-12663911,-12065183,4996454,-1256422,1073572,9583558,12851107,4003896,12673717 ), + new FieldElement( -1731589,-15155870,-3262930,16143082,19294135,13385325,14741514,-9103726,7903886,2348101 ), + new FieldElement( 24536016,-16515207,12715592,-3862155,1511293,10047386,-3842346,-7129159,-28377538,10048127 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -12622226,-6204820,30718825,2591312,-10617028,12192840,18873298,-7297090,-32297756,15221632 ), + new FieldElement( -26478122,-11103864,11546244,-1852483,9180880,7656409,-21343950,2095755,29769758,6593415 ), + new FieldElement( -31994208,-2907461,4176912,3264766,12538965,-868111,26312345,-6118678,30958054,8292160 ) + ), + new GroupElementPreComp( + new FieldElement( 31429822,-13959116,29173532,15632448,12174511,-2760094,32808831,3977186,26143136,-3148876 ), + new FieldElement( 22648901,1402143,-22799984,13746059,7936347,365344,-8668633,-1674433,-3758243,-2304625 ), + new FieldElement( -15491917,8012313,-2514730,-12702462,-23965846,-10254029,-1612713,-1535569,-16664475,8194478 ) + ), + new GroupElementPreComp( + new FieldElement( 27338066,-7507420,-7414224,10140405,-19026427,-6589889,27277191,8855376,28572286,3005164 ), + new FieldElement( 26287124,4821776,25476601,-4145903,-3764513,-15788984,-18008582,1182479,-26094821,-13079595 ), + new FieldElement( -7171154,3178080,23970071,6201893,-17195577,-4489192,-21876275,-13982627,32208683,-1198248 ) + ), + new GroupElementPreComp( + new FieldElement( -16657702,2817643,-10286362,14811298,6024667,13349505,-27315504,-10497842,-27672585,-11539858 ), + new FieldElement( 15941029,-9405932,-21367050,8062055,31876073,-238629,-15278393,-1444429,15397331,-4130193 ), + new FieldElement( 8934485,-13485467,-23286397,-13423241,-32446090,14047986,31170398,-1441021,-27505566,15087184 ) + ), + new GroupElementPreComp( + new FieldElement( -18357243,-2156491,24524913,-16677868,15520427,-6360776,-15502406,11461896,16788528,-5868942 ), + new FieldElement( -1947386,16013773,21750665,3714552,-17401782,-16055433,-3770287,-10323320,31322514,-11615635 ), + new FieldElement( 21426655,-5650218,-13648287,-5347537,-28812189,-4920970,-18275391,-14621414,13040862,-12112948 ) + ), + new GroupElementPreComp( + new FieldElement( 11293895,12478086,-27136401,15083750,-29307421,14748872,14555558,-13417103,1613711,4896935 ), + new FieldElement( -25894883,15323294,-8489791,-8057900,25967126,-13425460,2825960,-4897045,-23971776,-11267415 ), + new FieldElement( -15924766,-5229880,-17443532,6410664,3622847,10243618,20615400,12405433,-23753030,-8436416 ) + ), + new GroupElementPreComp( + new FieldElement( -7091295,12556208,-20191352,9025187,-17072479,4333801,4378436,2432030,23097949,-566018 ), + new FieldElement( 4565804,-16025654,20084412,-7842817,1724999,189254,24767264,10103221,-18512313,2424778 ), + new FieldElement( 366633,-11976806,8173090,-6890119,30788634,5745705,-7168678,1344109,-3642553,12412659 ) + ), + new GroupElementPreComp( + new FieldElement( -24001791,7690286,14929416,-168257,-32210835,-13412986,24162697,-15326504,-3141501,11179385 ), + new FieldElement( 18289522,-14724954,8056945,16430056,-21729724,7842514,-6001441,-1486897,-18684645,-11443503 ), + new FieldElement( 476239,6601091,-6152790,-9723375,17503545,-4863900,27672959,13403813,11052904,5219329 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 20678546,-8375738,-32671898,8849123,-5009758,14574752,31186971,-3973730,9014762,-8579056 ), + new FieldElement( -13644050,-10350239,-15962508,5075808,-1514661,-11534600,-33102500,9160280,8473550,-3256838 ), + new FieldElement( 24900749,14435722,17209120,-15292541,-22592275,9878983,-7689309,-16335821,-24568481,11788948 ) + ), + new GroupElementPreComp( + new FieldElement( -3118155,-11395194,-13802089,14797441,9652448,-6845904,-20037437,10410733,-24568470,-1458691 ), + new FieldElement( -15659161,16736706,-22467150,10215878,-9097177,7563911,11871841,-12505194,-18513325,8464118 ), + new FieldElement( -23400612,8348507,-14585951,-861714,-3950205,-6373419,14325289,8628612,33313881,-8370517 ) + ), + new GroupElementPreComp( + new FieldElement( -20186973,-4967935,22367356,5271547,-1097117,-4788838,-24805667,-10236854,-8940735,-5818269 ), + new FieldElement( -6948785,-1795212,-32625683,-16021179,32635414,-7374245,15989197,-12838188,28358192,-4253904 ), + new FieldElement( -23561781,-2799059,-32351682,-1661963,-9147719,10429267,-16637684,4072016,-5351664,5596589 ) + ), + new GroupElementPreComp( + new FieldElement( -28236598,-3390048,12312896,6213178,3117142,16078565,29266239,2557221,1768301,15373193 ), + new FieldElement( -7243358,-3246960,-4593467,-7553353,-127927,-912245,-1090902,-4504991,-24660491,3442910 ), + new FieldElement( -30210571,5124043,14181784,8197961,18964734,-11939093,22597931,7176455,-18585478,13365930 ) + ), + new GroupElementPreComp( + new FieldElement( -7877390,-1499958,8324673,4690079,6261860,890446,24538107,-8570186,-9689599,-3031667 ), + new FieldElement( 25008904,-10771599,-4305031,-9638010,16265036,15721635,683793,-11823784,15723479,-15163481 ), + new FieldElement( -9660625,12374379,-27006999,-7026148,-7724114,-12314514,11879682,5400171,519526,-1235876 ) + ), + new GroupElementPreComp( + new FieldElement( 22258397,-16332233,-7869817,14613016,-22520255,-2950923,-20353881,7315967,16648397,7605640 ), + new FieldElement( -8081308,-8464597,-8223311,9719710,19259459,-15348212,23994942,-5281555,-9468848,4763278 ), + new FieldElement( -21699244,9220969,-15730624,1084137,-25476107,-2852390,31088447,-7764523,-11356529,728112 ) + ), + new GroupElementPreComp( + new FieldElement( 26047220,-11751471,-6900323,-16521798,24092068,9158119,-4273545,-12555558,-29365436,-5498272 ), + new FieldElement( 17510331,-322857,5854289,8403524,17133918,-3112612,-28111007,12327945,10750447,10014012 ), + new FieldElement( -10312768,3936952,9156313,-8897683,16498692,-994647,-27481051,-666732,3424691,7540221 ) + ), + new GroupElementPreComp( + new FieldElement( 30322361,-6964110,11361005,-4143317,7433304,4989748,-7071422,-16317219,-9244265,15258046 ), + new FieldElement( 13054562,-2779497,19155474,469045,-12482797,4566042,5631406,2711395,1062915,-5136345 ), + new FieldElement( -19240248,-11254599,-29509029,-7499965,-5835763,13005411,-6066489,12194497,32960380,1459310 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 19852034,7027924,23669353,10020366,8586503,-6657907,394197,-6101885,18638003,-11174937 ), + new FieldElement( 31395534,15098109,26581030,8030562,-16527914,-5007134,9012486,-7584354,-6643087,-5442636 ), + new FieldElement( -9192165,-2347377,-1997099,4529534,25766844,607986,-13222,9677543,-32294889,-6456008 ) + ), + new GroupElementPreComp( + new FieldElement( -2444496,-149937,29348902,8186665,1873760,12489863,-30934579,-7839692,-7852844,-8138429 ), + new FieldElement( -15236356,-15433509,7766470,746860,26346930,-10221762,-27333451,10754588,-9431476,5203576 ), + new FieldElement( 31834314,14135496,-770007,5159118,20917671,-16768096,-7467973,-7337524,31809243,7347066 ) + ), + new GroupElementPreComp( + new FieldElement( -9606723,-11874240,20414459,13033986,13716524,-11691881,19797970,-12211255,15192876,-2087490 ), + new FieldElement( -12663563,-2181719,1168162,-3804809,26747877,-14138091,10609330,12694420,33473243,-13382104 ), + new FieldElement( 33184999,11180355,15832085,-11385430,-1633671,225884,15089336,-11023903,-6135662,14480053 ) + ), + new GroupElementPreComp( + new FieldElement( 31308717,-5619998,31030840,-1897099,15674547,-6582883,5496208,13685227,27595050,8737275 ), + new FieldElement( -20318852,-15150239,10933843,-16178022,8335352,-7546022,-31008351,-12610604,26498114,66511 ), + new FieldElement( 22644454,-8761729,-16671776,4884562,-3105614,-13559366,30540766,-4286747,-13327787,-7515095 ) + ), + new GroupElementPreComp( + new FieldElement( -28017847,9834845,18617207,-2681312,-3401956,-13307506,8205540,13585437,-17127465,15115439 ), + new FieldElement( 23711543,-672915,31206561,-8362711,6164647,-9709987,-33535882,-1426096,8236921,16492939 ), + new FieldElement( -23910559,-13515526,-26299483,-4503841,25005590,-7687270,19574902,10071562,6708380,-6222424 ) + ), + new GroupElementPreComp( + new FieldElement( 2101391,-4930054,19702731,2367575,-15427167,1047675,5301017,9328700,29955601,-11678310 ), + new FieldElement( 3096359,9271816,-21620864,-15521844,-14847996,-7592937,-25892142,-12635595,-9917575,6216608 ), + new FieldElement( -32615849,338663,-25195611,2510422,-29213566,-13820213,24822830,-6146567,-26767480,7525079 ) + ), + new GroupElementPreComp( + new FieldElement( -23066649,-13985623,16133487,-7896178,-3389565,778788,-910336,-2782495,-19386633,11994101 ), + new FieldElement( 21691500,-13624626,-641331,-14367021,3285881,-3483596,-25064666,9718258,-7477437,13381418 ), + new FieldElement( 18445390,-4202236,14979846,11622458,-1727110,-3582980,23111648,-6375247,28535282,15779576 ) + ), + new GroupElementPreComp( + new FieldElement( 30098053,3089662,-9234387,16662135,-21306940,11308411,-14068454,12021730,9955285,-16303356 ), + new FieldElement( 9734894,-14576830,-7473633,-9138735,2060392,11313496,-18426029,9924399,20194861,13380996 ), + new FieldElement( -26378102,-7965207,-22167821,15789297,-18055342,-6168792,-1984914,15707771,26342023,10146099 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( -26016874,-219943,21339191,-41388,19745256,-2878700,-29637280,2227040,21612326,-545728 ), + new FieldElement( -13077387,1184228,23562814,-5970442,-20351244,-6348714,25764461,12243797,-20856566,11649658 ), + new FieldElement( -10031494,11262626,27384172,2271902,26947504,-15997771,39944,6114064,33514190,2333242 ) + ), + new GroupElementPreComp( + new FieldElement( -21433588,-12421821,8119782,7219913,-21830522,-9016134,-6679750,-12670638,24350578,-13450001 ), + new FieldElement( -4116307,-11271533,-23886186,4843615,-30088339,690623,-31536088,-10406836,8317860,12352766 ), + new FieldElement( 18200138,-14475911,-33087759,-2696619,-23702521,-9102511,-23552096,-2287550,20712163,6719373 ) + ), + new GroupElementPreComp( + new FieldElement( 26656208,6075253,-7858556,1886072,-28344043,4262326,11117530,-3763210,26224235,-3297458 ), + new FieldElement( -17168938,-14854097,-3395676,-16369877,-19954045,14050420,21728352,9493610,18620611,-16428628 ), + new FieldElement( -13323321,13325349,11432106,5964811,18609221,6062965,-5269471,-9725556,-30701573,-16479657 ) + ), + new GroupElementPreComp( + new FieldElement( -23860538,-11233159,26961357,1640861,-32413112,-16737940,12248509,-5240639,13735342,1934062 ), + new FieldElement( 25089769,6742589,17081145,-13406266,21909293,-16067981,-15136294,-3765346,-21277997,5473616 ), + new FieldElement( 31883677,-7961101,1083432,-11572403,22828471,13290673,-7125085,12469656,29111212,-5451014 ) + ), + new GroupElementPreComp( + new FieldElement( 24244947,-15050407,-26262976,2791540,-14997599,16666678,24367466,6388839,-10295587,452383 ), + new FieldElement( -25640782,-3417841,5217916,16224624,19987036,-4082269,-24236251,-5915248,15766062,8407814 ), + new FieldElement( -20406999,13990231,15495425,16395525,5377168,15166495,-8917023,-4388953,-8067909,2276718 ) + ), + new GroupElementPreComp( + new FieldElement( 30157918,12924066,-17712050,9245753,19895028,3368142,-23827587,5096219,22740376,-7303417 ), + new FieldElement( 2041139,-14256350,7783687,13876377,-25946985,-13352459,24051124,13742383,-15637599,13295222 ), + new FieldElement( 33338237,-8505733,12532113,7977527,9106186,-1715251,-17720195,-4612972,-4451357,-14669444 ) + ), + new GroupElementPreComp( + new FieldElement( -20045281,5454097,-14346548,6447146,28862071,1883651,-2469266,-4141880,7770569,9620597 ), + new FieldElement( 23208068,7979712,33071466,8149229,1758231,-10834995,30945528,-1694323,-33502340,-14767970 ), + new FieldElement( 1439958,-16270480,-1079989,-793782,4625402,10647766,-5043801,1220118,30494170,-11440799 ) + ), + new GroupElementPreComp( + new FieldElement( -5037580,-13028295,-2970559,-3061767,15640974,-6701666,-26739026,926050,-1684339,-13333647 ), + new FieldElement( 13908495,-3549272,30919928,-6273825,-21521863,7989039,9021034,9078865,3353509,4033511 ), + new FieldElement( -29663431,-15113610,32259991,-344482,24295849,-12912123,23161163,8839127,27485041,7356032 ) + ), + }, + new[]{ + new GroupElementPreComp( + new FieldElement( 9661027,705443,11980065,-5370154,-1628543,14661173,-6346142,2625015,28431036,-16771834 ), + new FieldElement( -23839233,-8311415,-25945511,7480958,-17681669,-8354183,-22545972,14150565,15970762,4099461 ), + new FieldElement( 29262576,16756590,26350592,-8793563,8529671,-11208050,13617293,-9937143,11465739,8317062 ) + ), + new GroupElementPreComp( + new FieldElement( -25493081,-6962928,32500200,-9419051,-23038724,-2302222,14898637,3848455,20969334,-5157516 ), + new FieldElement( -20384450,-14347713,-18336405,13884722,-33039454,2842114,-21610826,-3649888,11177095,14989547 ), + new FieldElement( -24496721,-11716016,16959896,2278463,12066309,10137771,13515641,2581286,-28487508,9930240 ) + ), + new GroupElementPreComp( + new FieldElement( -17751622,-2097826,16544300,-13009300,-15914807,-14949081,18345767,-13403753,16291481,-5314038 ), + new FieldElement( -33229194,2553288,32678213,9875984,8534129,6889387,-9676774,6957617,4368891,9788741 ), + new FieldElement( 16660756,7281060,-10830758,12911820,20108584,-8101676,-21722536,-8613148,16250552,-11111103 ) + ), + new GroupElementPreComp( + new FieldElement( -19765507,2390526,-16551031,14161980,1905286,6414907,4689584,10604807,-30190403,4782747 ), + new FieldElement( -1354539,14736941,-7367442,-13292886,7710542,-14155590,-9981571,4383045,22546403,437323 ), + new FieldElement( 31665577,-12180464,-16186830,1491339,-18368625,3294682,27343084,2786261,-30633590,-14097016 ) + ), + new GroupElementPreComp( + new FieldElement( -14467279,-683715,-33374107,7448552,19294360,14334329,-19690631,2355319,-19284671,-6114373 ), + new FieldElement( 15121312,-15796162,6377020,-6031361,-10798111,-12957845,18952177,15496498,-29380133,11754228 ), + new FieldElement( -2637277,-13483075,8488727,-14303896,12728761,-1622493,7141596,11724556,22761615,-10134141 ) + ), + new GroupElementPreComp( + new FieldElement( 16918416,11729663,-18083579,3022987,-31015732,-13339659,-28741185,-12227393,32851222,11717399 ), + new FieldElement( 11166634,7338049,-6722523,4531520,-29468672,-7302055,31474879,3483633,-1193175,-4030831 ), + new FieldElement( -185635,9921305,31456609,-13536438,-12013818,13348923,33142652,6546660,-19985279,-3948376 ) + ), + new GroupElementPreComp( + new FieldElement( -32460596,11266712,-11197107,-7899103,31703694,3855903,-8537131,-12833048,-30772034,-15486313 ), + new FieldElement( -18006477,12709068,3991746,-6479188,-21491523,-10550425,-31135347,-16049879,10928917,3011958 ), + new FieldElement( -6957757,-15594337,31696059,334240,29576716,14796075,-30831056,-12805180,18008031,10258577 ) + ), + new GroupElementPreComp( + new FieldElement( -22448644,15655569,7018479,-4410003,-30314266,-1201591,-1853465,1367120,25127874,6671743 ), + new FieldElement( 29701166,-14373934,-10878120,9279288,-17568,13127210,21382910,11042292,25838796,4642684 ), + new FieldElement( -20430234,14955537,-24126347,8124619,-5369288,-5990470,30468147,-13900640,18423289,4177476 ) + ) + } + }; + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs new file mode 100644 index 000000000..cbd022b32 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs @@ -0,0 +1,50 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class LookupTables + { + internal static readonly GroupElementPreComp[] Base2 = new GroupElementPreComp[]{ + new GroupElementPreComp( + new FieldElement( 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 ), + new FieldElement( -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 ), + new FieldElement( -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 ) + ), + new GroupElementPreComp( + new FieldElement( 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 ), + new FieldElement( 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 ), + new FieldElement( 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 ) + ), + new GroupElementPreComp( + new FieldElement( 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 ), + new FieldElement( 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 ), + new FieldElement( 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 ) + ), + new GroupElementPreComp( + new FieldElement( 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 ), + new FieldElement( -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 ), + new FieldElement( 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 ) + ), + new GroupElementPreComp( + new FieldElement( -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 ), + new FieldElement( -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 ), + new FieldElement( 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 ) + ), + new GroupElementPreComp( + new FieldElement( -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 ), + new FieldElement( 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 ), + new FieldElement( 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 ) + ), + new GroupElementPreComp( + new FieldElement( -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 ), + new FieldElement( -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 ), + new FieldElement( -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 ) + ), + new GroupElementPreComp( + new FieldElement( -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 ), + new FieldElement( -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 ), + new FieldElement( -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 ) + ) + }; + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d.cs new file mode 100644 index 000000000..e341aeea0 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d.cs @@ -0,0 +1,9 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class LookupTables + { + internal static FieldElement d = new FieldElement(-10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs new file mode 100644 index 000000000..bf94d24dd --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs @@ -0,0 +1,9 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class LookupTables + { + internal static FieldElement d2 = new FieldElement(-21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199); + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs new file mode 100644 index 000000000..cf27c4da4 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs @@ -0,0 +1,12 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + internal static void fe_0(out FieldElement h) + { + h = default(FieldElement); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs new file mode 100644 index 000000000..348f919a7 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs @@ -0,0 +1,13 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + internal static void fe_1(out FieldElement h) + { + h = default(FieldElement); + h.x0 = 1; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_add.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_add.cs new file mode 100644 index 000000000..a3d2faaa8 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_add.cs @@ -0,0 +1,63 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* + h = f + g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + */ + //void fe_add(fe h,const fe f,const fe g) + internal static void fe_add(out FieldElement h, ref FieldElement f, ref FieldElement g) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 h0 = f0 + g0; + Int32 h1 = f1 + g1; + Int32 h2 = f2 + g2; + Int32 h3 = f3 + g3; + Int32 h4 = f4 + g4; + Int32 h5 = f5 + g5; + Int32 h6 = f6 + g6; + Int32 h7 = f7 + g7; + Int32 h8 = f8 + g8; + Int32 h9 = f9 + g9; + h.x0 = h0; + h.x1 = h1; + h.x2 = h2; + h.x3 = h3; + h.x4 = h4; + h.x5 = h5; + h.x6 = h6; + h.x7 = h7; + h.x8 = h8; + h.x9 = h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cmov.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cmov.cs new file mode 100644 index 000000000..a24a8efb5 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cmov.cs @@ -0,0 +1,70 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* + Replace (f,g) with (g,g) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. + */ + + //void fe_cmov(fe f,const fe g,unsigned int b) + internal static void fe_cmov(ref FieldElement f, ref FieldElement g, int b) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 x0 = f0 ^ g0; + Int32 x1 = f1 ^ g1; + Int32 x2 = f2 ^ g2; + Int32 x3 = f3 ^ g3; + Int32 x4 = f4 ^ g4; + Int32 x5 = f5 ^ g5; + Int32 x6 = f6 ^ g6; + Int32 x7 = f7 ^ g7; + Int32 x8 = f8 ^ g8; + Int32 x9 = f9 ^ g9; + b = -b; + x0 &= b; + x1 &= b; + x2 &= b; + x3 &= b; + x4 &= b; + x5 &= b; + x6 &= b; + x7 &= b; + x8 &= b; + x9 &= b; + f.x0 = f0 ^ x0; + f.x1 = f1 ^ x1; + f.x2 = f2 ^ x2; + f.x3 = f3 ^ x3; + f.x4 = f4 ^ x4; + f.x5 = f5 ^ x5; + f.x6 = f6 ^ x6; + f.x7 = f7 ^ x7; + f.x8 = f8 ^ x8; + f.x9 = f9 ^ x9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cswap.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cswap.cs new file mode 100644 index 000000000..f251eebc1 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_cswap.cs @@ -0,0 +1,78 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* + Replace (f,g) with (g,f) if b == 1; + replace (f,g) with (f,g) if b == 0. + + Preconditions: b in {0,1}. + */ + internal static void fe_cswap(ref FieldElement f, ref FieldElement g, uint b) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 x0 = f0 ^ g0; + Int32 x1 = f1 ^ g1; + Int32 x2 = f2 ^ g2; + Int32 x3 = f3 ^ g3; + Int32 x4 = f4 ^ g4; + Int32 x5 = f5 ^ g5; + Int32 x6 = f6 ^ g6; + Int32 x7 = f7 ^ g7; + Int32 x8 = f8 ^ g8; + Int32 x9 = f9 ^ g9; + int negb = unchecked((int)-b); + x0 &= negb; + x1 &= negb; + x2 &= negb; + x3 &= negb; + x4 &= negb; + x5 &= negb; + x6 &= negb; + x7 &= negb; + x8 &= negb; + x9 &= negb; + f.x0 = f0 ^ x0; + f.x1 = f1 ^ x1; + f.x2 = f2 ^ x2; + f.x3 = f3 ^ x3; + f.x4 = f4 ^ x4; + f.x5 = f5 ^ x5; + f.x6 = f6 ^ x6; + f.x7 = f7 ^ x7; + f.x8 = f8 ^ x8; + f.x9 = f9 ^ x9; + g.x0 = g0 ^ x0; + g.x1 = g1 ^ x1; + g.x2 = g2 ^ x2; + g.x3 = g3 ^ x3; + g.x4 = g4 ^ x4; + g.x5 = g5 ^ x5; + g.x6 = g6 ^ x6; + g.x7 = g7 ^ x7; + g.x8 = g8 ^ x8; + g.x9 = g9 ^ x9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_frombytes.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_frombytes.cs new file mode 100644 index 000000000..37abf8d5f --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_frombytes.cs @@ -0,0 +1,122 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + private static Int64 load_3(byte[] data, int offset) + { + uint result; + result = (uint)data[offset + 0]; + result |= (uint)data[offset + 1] << 8; + result |= (uint)data[offset + 2] << 16; + return (Int64)(UInt64)result; + } + + private static Int64 load_4(byte[] data, int offset) + { + uint result; + result = (uint)data[offset + 0]; + result |= (uint)data[offset + 1] << 8; + result |= (uint)data[offset + 2] << 16; + result |= (uint)data[offset + 3] << 24; + return (Int64)(UInt64)result; + } + + // Ignores top bit of h. + internal static void fe_frombytes(out FieldElement h, byte[] data, int offset) + { + Int64 h0 = load_4(data, offset); + Int64 h1 = load_3(data, offset + 4) << 6; + Int64 h2 = load_3(data, offset + 7) << 5; + Int64 h3 = load_3(data, offset + 10) << 3; + Int64 h4 = load_3(data, offset + 13) << 2; + Int64 h5 = load_4(data, offset + 16); + Int64 h6 = load_3(data, offset + 20) << 7; + Int64 h7 = load_3(data, offset + 23) << 5; + Int64 h8 = load_3(data, offset + 26) << 4; + Int64 h9 = (load_3(data, offset + 29) & 8388607) << 2; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h.x0 = (int)h0; + h.x1 = (int)h1; + h.x2 = (int)h2; + h.x3 = (int)h3; + h.x4 = (int)h4; + h.x5 = (int)h5; + h.x6 = (int)h6; + h.x7 = (int)h7; + h.x8 = (int)h8; + h.x9 = (int)h9; + } + + // does NOT ignore top bit + internal static void fe_frombytes2(out FieldElement h, byte[] data, int offset) + { + Int64 h0 = load_4(data, offset); + Int64 h1 = load_3(data, offset + 4) << 6; + Int64 h2 = load_3(data, offset + 7) << 5; + Int64 h3 = load_3(data, offset + 10) << 3; + Int64 h4 = load_3(data, offset + 13) << 2; + Int64 h5 = load_4(data, offset + 16); + Int64 h6 = load_3(data, offset + 20) << 7; + Int64 h7 = load_3(data, offset + 23) << 5; + Int64 h8 = load_3(data, offset + 26) << 4; + Int64 h9 = load_3(data, offset + 29) << 2; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h.x0 = (int)h0; + h.x1 = (int)h1; + h.x2 = (int)h2; + h.x3 = (int)h3; + h.x4 = (int)h4; + h.x5 = (int)h5; + h.x6 = (int)h6; + h.x7 = (int)h7; + h.x8 = (int)h8; + h.x9 = (int)h9; + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs new file mode 100644 index 000000000..8ff9a3a62 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs @@ -0,0 +1,179 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + internal static void fe_invert(out FieldElement result, ref FieldElement z) + { + FieldElement t0; + FieldElement t1; + FieldElement t2; + FieldElement t3; + int i; + + /* qhasm: fe z1 */ + + /* qhasm: fe z2 */ + + /* qhasm: fe z8 */ + + /* qhasm: fe z9 */ + + /* qhasm: fe z11 */ + + /* qhasm: fe z22 */ + + /* qhasm: fe z_5_0 */ + + /* qhasm: fe z_10_5 */ + + /* qhasm: fe z_10_0 */ + + /* qhasm: fe z_20_10 */ + + /* qhasm: fe z_20_0 */ + + /* qhasm: fe z_40_20 */ + + /* qhasm: fe z_40_0 */ + + /* qhasm: fe z_50_10 */ + + /* qhasm: fe z_50_0 */ + + /* qhasm: fe z_100_50 */ + + /* qhasm: fe z_100_0 */ + + /* qhasm: fe z_200_100 */ + + /* qhasm: fe z_200_0 */ + + /* qhasm: fe z_250_50 */ + + /* qhasm: fe z_250_0 */ + + /* qhasm: fe z_255_5 */ + + /* qhasm: fe z_255_21 */ + + /* qhasm: enter pow225521 */ + + /* qhasm: z2 = z1^2^1 */ + /* asm 1: fe_sq(>z2=fe#1,z2=fe#1,>z2=fe#1); */ + /* asm 2: fe_sq(>z2=t0,z2=t0,>z2=t0); */ + fe_sq(out t0, ref z); //for (i = 1; i < 1; ++i) fe_sq(out t0, ref t0); + + /* qhasm: z8 = z2^2^2 */ + /* asm 1: fe_sq(>z8=fe#2,z8=fe#2,>z8=fe#2); */ + /* asm 2: fe_sq(>z8=t1,z8=t1,>z8=t1); */ + fe_sq(out t1, ref t0); for (i = 1; i < 2; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z9 = z1*z8 */ + /* asm 1: fe_mul(>z9=fe#2,z9=t1,z11=fe#1,z11=t0,z22=fe#3,z22=fe#3,>z22=fe#3); */ + /* asm 2: fe_sq(>z22=t2,z22=t2,>z22=t2); */ + fe_sq(out t2, ref t0); //for (i = 1; i < 1; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_5_0 = z9*z22 */ + /* asm 1: fe_mul(>z_5_0=fe#2,z_5_0=t1,z_10_5=fe#3,z_10_5=fe#3,>z_10_5=fe#3); */ + /* asm 2: fe_sq(>z_10_5=t2,z_10_5=t2,>z_10_5=t2); */ + fe_sq(out t2, ref t1); for (i = 1; i < 5; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_10_0 = z_10_5*z_5_0 */ + /* asm 1: fe_mul(>z_10_0=fe#2,z_10_0=t1,z_20_10=fe#3,z_20_10=fe#3,>z_20_10=fe#3); */ + /* asm 2: fe_sq(>z_20_10=t2,z_20_10=t2,>z_20_10=t2); */ + fe_sq(out t2, ref t1); for (i = 1; i < 10; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_20_0 = z_20_10*z_10_0 */ + /* asm 1: fe_mul(>z_20_0=fe#3,z_20_0=t2,z_40_20=fe#4,z_40_20=fe#4,>z_40_20=fe#4); */ + /* asm 2: fe_sq(>z_40_20=t3,z_40_20=t3,>z_40_20=t3); */ + fe_sq(out t3, ref t2); for (i = 1; i < 20; ++i) fe_sq(out t3, ref t3); + + /* qhasm: z_40_0 = z_40_20*z_20_0 */ + /* asm 1: fe_mul(>z_40_0=fe#3,z_40_0=t2,z_50_10=fe#3,z_50_10=fe#3,>z_50_10=fe#3); */ + /* asm 2: fe_sq(>z_50_10=t2,z_50_10=t2,>z_50_10=t2); */ + fe_sq(out t2, ref t2); for (i = 1; i < 10; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_50_0 = z_50_10*z_10_0 */ + /* asm 1: fe_mul(>z_50_0=fe#2,z_50_0=t1,z_100_50=fe#3,z_100_50=fe#3,>z_100_50=fe#3); */ + /* asm 2: fe_sq(>z_100_50=t2,z_100_50=t2,>z_100_50=t2); */ + fe_sq(out t2, ref t1); for (i = 1; i < 50; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_100_0 = z_100_50*z_50_0 */ + /* asm 1: fe_mul(>z_100_0=fe#3,z_100_0=t2,z_200_100=fe#4,z_200_100=fe#4,>z_200_100=fe#4); */ + /* asm 2: fe_sq(>z_200_100=t3,z_200_100=t3,>z_200_100=t3); */ + fe_sq(out t3, ref t2); for (i = 1; i < 100; ++i) fe_sq(out t3, ref t3); + + /* qhasm: z_200_0 = z_200_100*z_100_0 */ + /* asm 1: fe_mul(>z_200_0=fe#3,z_200_0=t2,z_250_50=fe#3,z_250_50=fe#3,>z_250_50=fe#3); */ + /* asm 2: fe_sq(>z_250_50=t2,z_250_50=t2,>z_250_50=t2); */ + fe_sq(out t2, ref t2); for (i = 1; i < 50; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_250_0 = z_250_50*z_50_0 */ + /* asm 1: fe_mul(>z_250_0=fe#2,z_250_0=t1,z_255_5=fe#2,z_255_5=fe#2,>z_255_5=fe#2); */ + /* asm 2: fe_sq(>z_255_5=t1,z_255_5=t1,>z_255_5=t1); */ + fe_sq(out t1, ref t1); for (i = 1; i < 5; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z_255_21 = z_255_5*z11 */ + /* asm 1: fe_mul(>z_255_21=fe#12,z_255_21=out,> 31) ^ 1); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul.cs new file mode 100644 index 000000000..a060dba8a --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul.cs @@ -0,0 +1,258 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* + h = f * g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + + /* + Notes on implementation strategy: + + Using schoolbook multiplication. + Karatsuba would save a little in some cost models. + + Most multiplications by 2 and 19 are 32-bit precomputations; + cheaper than 64-bit postcomputations. + + There is one remaining multiplication by 19 in the carry chain; + one *19 precomputation can be merged into this, + but the resulting data flow is considerably less clean. + + There are 12 carries below. + 10 of them are 2-way parallelizable and vectorizable. + Can get away with 11 carries, but then data flow is much deeper. + + With tighter constraints on inputs can squeeze carries into int32. + */ + + internal static void fe_mul(out FieldElement h, ref FieldElement f, ref FieldElement g) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 g1_19 = 19 * g1; /* 1.959375*2^29 */ + Int32 g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + Int32 g3_19 = 19 * g3; + Int32 g4_19 = 19 * g4; + Int32 g5_19 = 19 * g5; + Int32 g6_19 = 19 * g6; + Int32 g7_19 = 19 * g7; + Int32 g8_19 = 19 * g8; + Int32 g9_19 = 19 * g9; + Int32 f1_2 = 2 * f1; + Int32 f3_2 = 2 * f3; + Int32 f5_2 = 2 * f5; + Int32 f7_2 = 2 * f7; + Int32 f9_2 = 2 * f9; + Int64 f0g0 = f0 * (Int64)g0; + Int64 f0g1 = f0 * (Int64)g1; + Int64 f0g2 = f0 * (Int64)g2; + Int64 f0g3 = f0 * (Int64)g3; + Int64 f0g4 = f0 * (Int64)g4; + Int64 f0g5 = f0 * (Int64)g5; + Int64 f0g6 = f0 * (Int64)g6; + Int64 f0g7 = f0 * (Int64)g7; + Int64 f0g8 = f0 * (Int64)g8; + Int64 f0g9 = f0 * (Int64)g9; + Int64 f1g0 = f1 * (Int64)g0; + Int64 f1g1_2 = f1_2 * (Int64)g1; + Int64 f1g2 = f1 * (Int64)g2; + Int64 f1g3_2 = f1_2 * (Int64)g3; + Int64 f1g4 = f1 * (Int64)g4; + Int64 f1g5_2 = f1_2 * (Int64)g5; + Int64 f1g6 = f1 * (Int64)g6; + Int64 f1g7_2 = f1_2 * (Int64)g7; + Int64 f1g8 = f1 * (Int64)g8; + Int64 f1g9_38 = f1_2 * (Int64)g9_19; + Int64 f2g0 = f2 * (Int64)g0; + Int64 f2g1 = f2 * (Int64)g1; + Int64 f2g2 = f2 * (Int64)g2; + Int64 f2g3 = f2 * (Int64)g3; + Int64 f2g4 = f2 * (Int64)g4; + Int64 f2g5 = f2 * (Int64)g5; + Int64 f2g6 = f2 * (Int64)g6; + Int64 f2g7 = f2 * (Int64)g7; + Int64 f2g8_19 = f2 * (Int64)g8_19; + Int64 f2g9_19 = f2 * (Int64)g9_19; + Int64 f3g0 = f3 * (Int64)g0; + Int64 f3g1_2 = f3_2 * (Int64)g1; + Int64 f3g2 = f3 * (Int64)g2; + Int64 f3g3_2 = f3_2 * (Int64)g3; + Int64 f3g4 = f3 * (Int64)g4; + Int64 f3g5_2 = f3_2 * (Int64)g5; + Int64 f3g6 = f3 * (Int64)g6; + Int64 f3g7_38 = f3_2 * (Int64)g7_19; + Int64 f3g8_19 = f3 * (Int64)g8_19; + Int64 f3g9_38 = f3_2 * (Int64)g9_19; + Int64 f4g0 = f4 * (Int64)g0; + Int64 f4g1 = f4 * (Int64)g1; + Int64 f4g2 = f4 * (Int64)g2; + Int64 f4g3 = f4 * (Int64)g3; + Int64 f4g4 = f4 * (Int64)g4; + Int64 f4g5 = f4 * (Int64)g5; + Int64 f4g6_19 = f4 * (Int64)g6_19; + Int64 f4g7_19 = f4 * (Int64)g7_19; + Int64 f4g8_19 = f4 * (Int64)g8_19; + Int64 f4g9_19 = f4 * (Int64)g9_19; + Int64 f5g0 = f5 * (Int64)g0; + Int64 f5g1_2 = f5_2 * (Int64)g1; + Int64 f5g2 = f5 * (Int64)g2; + Int64 f5g3_2 = f5_2 * (Int64)g3; + Int64 f5g4 = f5 * (Int64)g4; + Int64 f5g5_38 = f5_2 * (Int64)g5_19; + Int64 f5g6_19 = f5 * (Int64)g6_19; + Int64 f5g7_38 = f5_2 * (Int64)g7_19; + Int64 f5g8_19 = f5 * (Int64)g8_19; + Int64 f5g9_38 = f5_2 * (Int64)g9_19; + Int64 f6g0 = f6 * (Int64)g0; + Int64 f6g1 = f6 * (Int64)g1; + Int64 f6g2 = f6 * (Int64)g2; + Int64 f6g3 = f6 * (Int64)g3; + Int64 f6g4_19 = f6 * (Int64)g4_19; + Int64 f6g5_19 = f6 * (Int64)g5_19; + Int64 f6g6_19 = f6 * (Int64)g6_19; + Int64 f6g7_19 = f6 * (Int64)g7_19; + Int64 f6g8_19 = f6 * (Int64)g8_19; + Int64 f6g9_19 = f6 * (Int64)g9_19; + Int64 f7g0 = f7 * (Int64)g0; + Int64 f7g1_2 = f7_2 * (Int64)g1; + Int64 f7g2 = f7 * (Int64)g2; + Int64 f7g3_38 = f7_2 * (Int64)g3_19; + Int64 f7g4_19 = f7 * (Int64)g4_19; + Int64 f7g5_38 = f7_2 * (Int64)g5_19; + Int64 f7g6_19 = f7 * (Int64)g6_19; + Int64 f7g7_38 = f7_2 * (Int64)g7_19; + Int64 f7g8_19 = f7 * (Int64)g8_19; + Int64 f7g9_38 = f7_2 * (Int64)g9_19; + Int64 f8g0 = f8 * (Int64)g0; + Int64 f8g1 = f8 * (Int64)g1; + Int64 f8g2_19 = f8 * (Int64)g2_19; + Int64 f8g3_19 = f8 * (Int64)g3_19; + Int64 f8g4_19 = f8 * (Int64)g4_19; + Int64 f8g5_19 = f8 * (Int64)g5_19; + Int64 f8g6_19 = f8 * (Int64)g6_19; + Int64 f8g7_19 = f8 * (Int64)g7_19; + Int64 f8g8_19 = f8 * (Int64)g8_19; + Int64 f8g9_19 = f8 * (Int64)g9_19; + Int64 f9g0 = f9 * (Int64)g0; + Int64 f9g1_38 = f9_2 * (Int64)g1_19; + Int64 f9g2_19 = f9 * (Int64)g2_19; + Int64 f9g3_38 = f9_2 * (Int64)g3_19; + Int64 f9g4_19 = f9 * (Int64)g4_19; + Int64 f9g5_38 = f9_2 * (Int64)g5_19; + Int64 f9g6_19 = f9 * (Int64)g6_19; + Int64 f9g7_38 = f9_2 * (Int64)g7_19; + Int64 f9g8_19 = f9 * (Int64)g8_19; + Int64 f9g9_38 = f9_2 * (Int64)g9_19; + Int64 h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; + Int64 h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19; + Int64 h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38; + Int64 h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19; + Int64 h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38; + Int64 h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19; + Int64 h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38; + Int64 h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19; + Int64 h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38; + Int64 h9 = f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + /* + |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38)) + i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8 + |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19)) + i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9 + */ + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + /* |h0| <= 2^25 */ + /* |h4| <= 2^25 */ + /* |h1| <= 1.71*2^59 */ + /* |h5| <= 1.71*2^59 */ + + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + /* |h1| <= 2^24; from now on fits into int32 */ + /* |h5| <= 2^24; from now on fits into int32 */ + /* |h2| <= 1.41*2^60 */ + /* |h6| <= 1.41*2^60 */ + + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + /* |h2| <= 2^25; from now on fits into int32 unchanged */ + /* |h6| <= 2^25; from now on fits into int32 unchanged */ + /* |h3| <= 1.71*2^59 */ + /* |h7| <= 1.71*2^59 */ + + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + /* |h3| <= 2^24; from now on fits into int32 unchanged */ + /* |h7| <= 2^24; from now on fits into int32 unchanged */ + /* |h4| <= 1.72*2^34 */ + /* |h8| <= 1.41*2^60 */ + + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + /* |h4| <= 2^25; from now on fits into int32 unchanged */ + /* |h8| <= 2^25; from now on fits into int32 unchanged */ + /* |h5| <= 1.01*2^24 */ + /* |h9| <= 1.71*2^59 */ + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + /* |h9| <= 2^24; from now on fits into int32 unchanged */ + /* |h0| <= 1.1*2^39 */ + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + /* |h0| <= 2^25; from now on fits into int32 unchanged */ + /* |h1| <= 1.01*2^24 */ + + h.x0 = (Int32)h0; + h.x1 = (Int32)h1; + h.x2 = (Int32)h2; + h.x3 = (Int32)h3; + h.x4 = (Int32)h4; + h.x5 = (Int32)h5; + h.x6 = (Int32)h6; + h.x7 = (Int32)h7; + h.x8 = (Int32)h8; + h.x9 = (Int32)h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul121666.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul121666.cs new file mode 100644 index 000000000..5112f5274 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_mul121666.cs @@ -0,0 +1,76 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + + /* + h = f * 121666 + Can overlap h with f. + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + + Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + */ + + internal static void fe_mul121666(out FieldElement h, ref FieldElement f) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int64 h0 = f0 * (Int64)121666; + Int64 h1 = f1 * (Int64)121666; + Int64 h2 = f2 * (Int64)121666; + Int64 h3 = f3 * (Int64)121666; + Int64 h4 = f4 * (Int64)121666; + Int64 h5 = f5 * (Int64)121666; + Int64 h6 = f6 * (Int64)121666; + Int64 h7 = f7 * (Int64)121666; + Int64 h8 = f8 * (Int64)121666; + Int64 h9 = f9 * (Int64)121666; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + h.x0 = (int)h0; + h.x1 = (int)h1; + h.x2 = (int)h2; + h.x3 = (int)h3; + h.x4 = (int)h4; + h.x5 = (int)h5; + h.x6 = (int)h6; + h.x7 = (int)h7; + h.x8 = (int)h8; + h.x9 = (int)h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_neg.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_neg.cs new file mode 100644 index 000000000..f73bd4a59 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_neg.cs @@ -0,0 +1,50 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* + h = -f + + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + + Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + */ + internal static void fe_neg(out FieldElement h, ref FieldElement f) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 h0 = -f0; + Int32 h1 = -f1; + Int32 h2 = -f2; + Int32 h3 = -f3; + Int32 h4 = -f4; + Int32 h5 = -f5; + Int32 h6 = -f6; + Int32 h7 = -f7; + Int32 h8 = -f8; + Int32 h9 = -f9; + h.x0 = h0; + h.x1 = h1; + h.x2 = h2; + h.x3 = h3; + h.x4 = h4; + h.x5 = h5; + h.x6 = h6; + h.x7 = h7; + h.x8 = h8; + h.x9 = h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_pow22523.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_pow22523.cs new file mode 100644 index 000000000..0686e2d5b --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_pow22523.cs @@ -0,0 +1,175 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + internal static void fe_pow22523(out FieldElement result, ref FieldElement z) + { + FieldElement t0; + FieldElement t1; + FieldElement t2; + int i; + + /* qhasm: fe z1 */ + + /* qhasm: fe z2 */ + + /* qhasm: fe z8 */ + + /* qhasm: fe z9 */ + + /* qhasm: fe z11 */ + + /* qhasm: fe z22 */ + + /* qhasm: fe z_5_0 */ + + /* qhasm: fe z_10_5 */ + + /* qhasm: fe z_10_0 */ + + /* qhasm: fe z_20_10 */ + + /* qhasm: fe z_20_0 */ + + /* qhasm: fe z_40_20 */ + + /* qhasm: fe z_40_0 */ + + /* qhasm: fe z_50_10 */ + + /* qhasm: fe z_50_0 */ + + /* qhasm: fe z_100_50 */ + + /* qhasm: fe z_100_0 */ + + /* qhasm: fe z_200_100 */ + + /* qhasm: fe z_200_0 */ + + /* qhasm: fe z_250_50 */ + + /* qhasm: fe z_250_0 */ + + /* qhasm: fe z_252_2 */ + + /* qhasm: fe z_252_3 */ + + /* qhasm: enter pow22523 */ + + /* qhasm: z2 = z1^2^1 */ + /* asm 1: fe_sq(>z2=fe#1,z2=fe#1,>z2=fe#1); */ + /* asm 2: fe_sq(>z2=t0,z2=t0,>z2=t0); */ + fe_sq(out t0, ref z); //for (i = 1; i < 1; ++i) fe_sq(out t0, ref t0); + + /* qhasm: z8 = z2^2^2 */ + /* asm 1: fe_sq(>z8=fe#2,z8=fe#2,>z8=fe#2); */ + /* asm 2: fe_sq(>z8=t1,z8=t1,>z8=t1); */ + fe_sq(out t1, ref t0); for (i = 1; i < 2; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z9 = z1*z8 */ + /* asm 1: fe_mul(>z9=fe#2,z9=t1,z11=fe#1,z11=t0,z22=fe#1,z22=fe#1,>z22=fe#1); */ + /* asm 2: fe_sq(>z22=t0,z22=t0,>z22=t0); */ + fe_sq(out t0, ref t0); //for (i = 1; i < 1; ++i) fe_sq(out t0, ref t0); + + /* qhasm: z_5_0 = z9*z22 */ + /* asm 1: fe_mul(>z_5_0=fe#1,z_5_0=t0,z_10_5=fe#2,z_10_5=fe#2,>z_10_5=fe#2); */ + /* asm 2: fe_sq(>z_10_5=t1,z_10_5=t1,>z_10_5=t1); */ + fe_sq(out t1, ref t0); for (i = 1; i < 5; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z_10_0 = z_10_5*z_5_0 */ + /* asm 1: fe_mul(>z_10_0=fe#1,z_10_0=t0,z_20_10=fe#2,z_20_10=fe#2,>z_20_10=fe#2); */ + /* asm 2: fe_sq(>z_20_10=t1,z_20_10=t1,>z_20_10=t1); */ + fe_sq(out t1, ref t0); for (i = 1; i < 10; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z_20_0 = z_20_10*z_10_0 */ + /* asm 1: fe_mul(>z_20_0=fe#2,z_20_0=t1,z_40_20=fe#3,z_40_20=fe#3,>z_40_20=fe#3); */ + /* asm 2: fe_sq(>z_40_20=t2,z_40_20=t2,>z_40_20=t2); */ + fe_sq(out t2, ref t1); for (i = 1; i < 20; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_40_0 = z_40_20*z_20_0 */ + /* asm 1: fe_mul(>z_40_0=fe#2,z_40_0=t1,z_50_10=fe#2,z_50_10=fe#2,>z_50_10=fe#2); */ + /* asm 2: fe_sq(>z_50_10=t1,z_50_10=t1,>z_50_10=t1); */ + fe_sq(out t1, ref t1); for (i = 1; i < 10; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z_50_0 = z_50_10*z_10_0 */ + /* asm 1: fe_mul(>z_50_0=fe#1,z_50_0=t0,z_100_50=fe#2,z_100_50=fe#2,>z_100_50=fe#2); */ + /* asm 2: fe_sq(>z_100_50=t1,z_100_50=t1,>z_100_50=t1); */ + fe_sq(out t1, ref t0); for (i = 1; i < 50; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z_100_0 = z_100_50*z_50_0 */ + /* asm 1: fe_mul(>z_100_0=fe#2,z_100_0=t1,z_200_100=fe#3,z_200_100=fe#3,>z_200_100=fe#3); */ + /* asm 2: fe_sq(>z_200_100=t2,z_200_100=t2,>z_200_100=t2); */ + fe_sq(out t2, ref t1); for (i = 1; i < 100; ++i) fe_sq(out t2, ref t2); + + /* qhasm: z_200_0 = z_200_100*z_100_0 */ + /* asm 1: fe_mul(>z_200_0=fe#2,z_200_0=t1,z_250_50=fe#2,z_250_50=fe#2,>z_250_50=fe#2); */ + /* asm 2: fe_sq(>z_250_50=t1,z_250_50=t1,>z_250_50=t1); */ + fe_sq(out t1, ref t1); for (i = 1; i < 50; ++i) fe_sq(out t1, ref t1); + + /* qhasm: z_250_0 = z_250_50*z_50_0 */ + /* asm 1: fe_mul(>z_250_0=fe#1,z_250_0=t0,z_252_2=fe#1,z_252_2=fe#1,>z_252_2=fe#1); */ + /* asm 2: fe_sq(>z_252_2=t0,z_252_2=t0,>z_252_2=t0); */ + fe_sq(out t0, ref t0); for (i = 1; i < 2; ++i) fe_sq(out t0, ref t0); + + /* qhasm: z_252_3 = z_252_2*z1 */ + /* asm 1: fe_mul(>z_252_3=fe#12,z_252_3=out,> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h.x0 = (Int32)h0; + h.x1 = (Int32)h1; + h.x2 = (Int32)h2; + h.x3 = (Int32)h3; + h.x4 = (Int32)h4; + h.x5 = (Int32)h5; + h.x6 = (Int32)h6; + h.x7 = (Int32)h7; + h.x8 = (Int32)h8; + h.x9 = (Int32)h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sq2.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sq2.cs new file mode 100644 index 000000000..a869311b4 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sq2.cs @@ -0,0 +1,164 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* +h = 2 * f * f +Can overlap h with f. + +Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + +Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. +*/ + + /* + See fe_mul.c for discussion of implementation strategy. + */ + internal static void fe_sq2(out FieldElement h, ref FieldElement f) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 f0_2 = 2 * f0; + Int32 f1_2 = 2 * f1; + Int32 f2_2 = 2 * f2; + Int32 f3_2 = 2 * f3; + Int32 f4_2 = 2 * f4; + Int32 f5_2 = 2 * f5; + Int32 f6_2 = 2 * f6; + Int32 f7_2 = 2 * f7; + Int32 f5_38 = 38 * f5; /* 1.959375*2^30 */ + Int32 f6_19 = 19 * f6; /* 1.959375*2^30 */ + Int32 f7_38 = 38 * f7; /* 1.959375*2^30 */ + Int32 f8_19 = 19 * f8; /* 1.959375*2^30 */ + Int32 f9_38 = 38 * f9; /* 1.959375*2^30 */ + Int64 f0f0 = f0 * (Int64)f0; + Int64 f0f1_2 = f0_2 * (Int64)f1; + Int64 f0f2_2 = f0_2 * (Int64)f2; + Int64 f0f3_2 = f0_2 * (Int64)f3; + Int64 f0f4_2 = f0_2 * (Int64)f4; + Int64 f0f5_2 = f0_2 * (Int64)f5; + Int64 f0f6_2 = f0_2 * (Int64)f6; + Int64 f0f7_2 = f0_2 * (Int64)f7; + Int64 f0f8_2 = f0_2 * (Int64)f8; + Int64 f0f9_2 = f0_2 * (Int64)f9; + Int64 f1f1_2 = f1_2 * (Int64)f1; + Int64 f1f2_2 = f1_2 * (Int64)f2; + Int64 f1f3_4 = f1_2 * (Int64)f3_2; + Int64 f1f4_2 = f1_2 * (Int64)f4; + Int64 f1f5_4 = f1_2 * (Int64)f5_2; + Int64 f1f6_2 = f1_2 * (Int64)f6; + Int64 f1f7_4 = f1_2 * (Int64)f7_2; + Int64 f1f8_2 = f1_2 * (Int64)f8; + Int64 f1f9_76 = f1_2 * (Int64)f9_38; + Int64 f2f2 = f2 * (Int64)f2; + Int64 f2f3_2 = f2_2 * (Int64)f3; + Int64 f2f4_2 = f2_2 * (Int64)f4; + Int64 f2f5_2 = f2_2 * (Int64)f5; + Int64 f2f6_2 = f2_2 * (Int64)f6; + Int64 f2f7_2 = f2_2 * (Int64)f7; + Int64 f2f8_38 = f2_2 * (Int64)f8_19; + Int64 f2f9_38 = f2 * (Int64)f9_38; + Int64 f3f3_2 = f3_2 * (Int64)f3; + Int64 f3f4_2 = f3_2 * (Int64)f4; + Int64 f3f5_4 = f3_2 * (Int64)f5_2; + Int64 f3f6_2 = f3_2 * (Int64)f6; + Int64 f3f7_76 = f3_2 * (Int64)f7_38; + Int64 f3f8_38 = f3_2 * (Int64)f8_19; + Int64 f3f9_76 = f3_2 * (Int64)f9_38; + Int64 f4f4 = f4 * (Int64)f4; + Int64 f4f5_2 = f4_2 * (Int64)f5; + Int64 f4f6_38 = f4_2 * (Int64)f6_19; + Int64 f4f7_38 = f4 * (Int64)f7_38; + Int64 f4f8_38 = f4_2 * (Int64)f8_19; + Int64 f4f9_38 = f4 * (Int64)f9_38; + Int64 f5f5_38 = f5 * (Int64)f5_38; + Int64 f5f6_38 = f5_2 * (Int64)f6_19; + Int64 f5f7_76 = f5_2 * (Int64)f7_38; + Int64 f5f8_38 = f5_2 * (Int64)f8_19; + Int64 f5f9_76 = f5_2 * (Int64)f9_38; + Int64 f6f6_19 = f6 * (Int64)f6_19; + Int64 f6f7_38 = f6 * (Int64)f7_38; + Int64 f6f8_38 = f6_2 * (Int64)f8_19; + Int64 f6f9_38 = f6 * (Int64)f9_38; + Int64 f7f7_38 = f7 * (Int64)f7_38; + Int64 f7f8_38 = f7_2 * (Int64)f8_19; + Int64 f7f9_76 = f7_2 * (Int64)f9_38; + Int64 f8f8_19 = f8 * (Int64)f8_19; + Int64 f8f9_38 = f8 * (Int64)f9_38; + Int64 f9f9_38 = f9 * (Int64)f9_38; + Int64 h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + Int64 h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + Int64 h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + Int64 h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + Int64 h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + Int64 h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + Int64 h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + Int64 h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + Int64 h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + Int64 h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + + carry1 = (h1 + (Int64)(1 << 24)) >> 25; h2 += carry1; h1 -= carry1 << 25; + carry5 = (h5 + (Int64)(1 << 24)) >> 25; h6 += carry5; h5 -= carry5 << 25; + + carry2 = (h2 + (Int64)(1 << 25)) >> 26; h3 += carry2; h2 -= carry2 << 26; + carry6 = (h6 + (Int64)(1 << 25)) >> 26; h7 += carry6; h6 -= carry6 << 26; + + carry3 = (h3 + (Int64)(1 << 24)) >> 25; h4 += carry3; h3 -= carry3 << 25; + carry7 = (h7 + (Int64)(1 << 24)) >> 25; h8 += carry7; h7 -= carry7 << 25; + + carry4 = (h4 + (Int64)(1 << 25)) >> 26; h5 += carry4; h4 -= carry4 << 26; + carry8 = (h8 + (Int64)(1 << 25)) >> 26; h9 += carry8; h8 -= carry8 << 26; + + carry9 = (h9 + (Int64)(1 << 24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; + + carry0 = (h0 + (Int64)(1 << 25)) >> 26; h1 += carry0; h0 -= carry0 << 26; + + h.x0 = (Int32)h0; + h.x1 = (Int32)h1; + h.x2 = (Int32)h2; + h.x3 = (Int32)h3; + h.x4 = (Int32)h4; + h.x5 = (Int32)h5; + h.x6 = (Int32)h6; + h.x7 = (Int32)h7; + h.x8 = (Int32)h8; + h.x9 = (Int32)h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sub.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sub.cs new file mode 100644 index 000000000..7eec1c604 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_sub.cs @@ -0,0 +1,63 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* + h = f - g + Can overlap h with f or g. + + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + */ + + internal static void fe_sub(out FieldElement h, ref FieldElement f, ref FieldElement g) + { + Int32 f0 = f.x0; + Int32 f1 = f.x1; + Int32 f2 = f.x2; + Int32 f3 = f.x3; + Int32 f4 = f.x4; + Int32 f5 = f.x5; + Int32 f6 = f.x6; + Int32 f7 = f.x7; + Int32 f8 = f.x8; + Int32 f9 = f.x9; + Int32 g0 = g.x0; + Int32 g1 = g.x1; + Int32 g2 = g.x2; + Int32 g3 = g.x3; + Int32 g4 = g.x4; + Int32 g5 = g.x5; + Int32 g6 = g.x6; + Int32 g7 = g.x7; + Int32 g8 = g.x8; + Int32 g9 = g.x9; + Int32 h0 = f0 - g0; + Int32 h1 = f1 - g1; + Int32 h2 = f2 - g2; + Int32 h3 = f3 - g3; + Int32 h4 = f4 - g4; + Int32 h5 = f5 - g5; + Int32 h6 = f6 - g6; + Int32 h7 = f7 - g7; + Int32 h8 = f8 - g8; + Int32 h9 = f9 - g9; + h.x0 = h0; + h.x1 = h1; + h.x2 = h2; + h.x3 = h3; + h.x4 = h4; + h.x5 = h5; + h.x6 = h6; + h.x7 = h7; + h.x8 = h8; + h.x9 = h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_tobytes.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_tobytes.cs new file mode 100644 index 000000000..27167e41c --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/fe_tobytes.cs @@ -0,0 +1,154 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class FieldOperations + { + /* + Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + + Write p=2^255-19; q=floor(h/p). + Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + + Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 0); + s[offset + 1] = (byte) (h0 >> 8); + s[offset + 2] = (byte) (h0 >> 16); + s[offset + 3] = (byte) ((h0 >> 24) | (h1 << 2)); + s[offset + 4] = (byte) (h1 >> 6); + s[offset + 5] = (byte) (h1 >> 14); + s[offset + 6] = (byte) ((h1 >> 22) | (h2 << 3)); + s[offset + 7] = (byte) (h2 >> 5); + s[offset + 8] = (byte) (h2 >> 13); + s[offset + 9] = (byte) ((h2 >> 21) | (h3 << 5)); + s[offset + 10] = (byte) (h3 >> 3); + s[offset + 11] = (byte) (h3 >> 11); + s[offset + 12] = (byte) ((h3 >> 19) | (h4 << 6)); + s[offset + 13] = (byte) (h4 >> 2); + s[offset + 14] = (byte) (h4 >> 10); + s[offset + 15] = (byte) (h4 >> 18); + s[offset + 16] = (byte) (h5 >> 0); + s[offset + 17] = (byte) (h5 >> 8); + s[offset + 18] = (byte) (h5 >> 16); + s[offset + 19] = (byte) ((h5 >> 24) | (h6 << 1)); + s[offset + 20] = (byte) (h6 >> 7); + s[offset + 21] = (byte) (h6 >> 15); + s[offset + 22] = (byte) ((h6 >> 23) | (h7 << 3)); + s[offset + 23] = (byte) (h7 >> 5); + s[offset + 24] = (byte) (h7 >> 13); + s[offset + 25] = (byte) ((h7 >> 21) | (h8 << 4)); + s[offset + 26] = (byte) (h8 >> 4); + s[offset + 27] = (byte) (h8 >> 12); + s[offset + 28] = (byte) ((h8 >> 20) | (h9 << 6)); + s[offset + 29] = (byte) (h9 >> 2); + s[offset + 30] = (byte) (h9 >> 10); + s[offset + 31] = (byte) (h9 >> 18); + } + } + + internal static void fe_reduce(out FieldElement hr, ref FieldElement h) + { + Int32 h0 = h.x0; + Int32 h1 = h.x1; + Int32 h2 = h.x2; + Int32 h3 = h.x3; + Int32 h4 = h.x4; + Int32 h5 = h.x5; + Int32 h6 = h.x6; + Int32 h7 = h.x7; + Int32 h8 = h.x8; + Int32 h9 = h.x9; + Int32 q; + Int32 carry0; + Int32 carry1; + Int32 carry2; + Int32 carry3; + Int32 carry4; + Int32 carry5; + Int32 carry6; + Int32 carry7; + Int32 carry8; + Int32 carry9; + + q = (19 * h9 + (((Int32)1) << 24)) >> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + + carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; + carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; + carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; + carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; + carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; + carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; + carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; + carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; + carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; + carry9 = h9 >> 25; h9 -= carry9 << 25; + /* h10 = carry9 */ + + hr.x0 = h0; + hr.x1 = h1; + hr.x2 = h2; + hr.x3 = h3; + hr.x4 = h4; + hr.x5 = h5; + hr.x6 = h6; + hr.x7 = h7; + hr.x8 = h8; + hr.x9 = h9; + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_add.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_add.cs new file mode 100644 index 000000000..9b1129c12 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_add.cs @@ -0,0 +1,113 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class GroupOperations + { + /* + r = p + q + */ + + internal static void ge_add(out GroupElementP1P1 r, ref GroupElementP3 p, ref GroupElementCached q) + { + FieldElement t0; + + /* qhasm: enter GroupElementadd */ + + /* qhasm: fe X1 */ + + /* qhasm: fe Y1 */ + + /* qhasm: fe Z1 */ + + /* qhasm: fe Z2 */ + + /* qhasm: fe T1 */ + + /* qhasm: fe ZZ */ + + /* qhasm: fe YpX2 */ + + /* qhasm: fe YmX2 */ + + /* qhasm: fe T2d2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Y3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe T3 */ + + /* qhasm: fe YpX1 */ + + /* qhasm: fe YmX1 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: YpX1 = Y1+X1 */ + /* asm 1: fe_add(>YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,ZZ=fe#1,ZZ=r.X,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,> 3] >> (i & 7))); + + for (i = 0; i < 256; ++i) + if (r[i] != 0) + { + for (b = 1; b <= 6 && i + b < 256; ++b) + { + if (r[i + b] != 0) + { + if (r[i] + (r[i + b] << b) <= 15) + { + r[i] += (sbyte)(r[i + b] << b); r[i + b] = 0; + } + else if (r[i] - (r[i + b] << b) >= -15) + { + r[i] -= (sbyte)(r[i + b] << b); + for (k = i + b; k < 256; ++k) + { + if (r[k] == 0) + { + r[k] = 1; + break; + } + r[k] = 0; + } + } + else + break; + } + } + } + + } + + /* + r = a * A + b * B + where a = a[0]+256*a[1]+...+256^31 a[31]. + and b = b[0]+256*b[1]+...+256^31 b[31]. + B is the Ed25519 base point (x,4/5) with x positive. + */ + + internal static void ge_double_scalarmult_vartime(out GroupElementP2 r, byte[] a, ref GroupElementP3 A, byte[] b) + { + GroupElementPreComp[] Bi = LookupTables.Base2; + // todo: Perhaps remove these allocations? + sbyte[] aslide = new sbyte[256]; + sbyte[] bslide = new sbyte[256]; + GroupElementCached[] Ai = new GroupElementCached[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + GroupElementP1P1 t; + GroupElementP3 u; + GroupElementP3 A2; + int i; + + slide(aslide, a); + slide(bslide, b); + + ge_p3_to_cached(out Ai[0], ref A); + ge_p3_dbl(out t, ref A); ge_p1p1_to_p3(out A2, ref t); + ge_add(out t, ref A2, ref Ai[0]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[1], ref u); + ge_add(out t, ref A2, ref Ai[1]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[2], ref u); + ge_add(out t, ref A2, ref Ai[2]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[3], ref u); + ge_add(out t, ref A2, ref Ai[3]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[4], ref u); + ge_add(out t, ref A2, ref Ai[4]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[5], ref u); + ge_add(out t, ref A2, ref Ai[5]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[6], ref u); + ge_add(out t, ref A2, ref Ai[6]); ge_p1p1_to_p3(out u, ref t); ge_p3_to_cached(out Ai[7], ref u); + + ge_p2_0(out r); + + for (i = 255; i >= 0; --i) + { + if ((aslide[i] != 0) || (bslide[i] != 0)) break; + } + + for (; i >= 0; --i) + { + ge_p2_dbl(out t, ref r); + + if (aslide[i] > 0) + { + ge_p1p1_to_p3(out u, ref t); + ge_add(out t, ref u, ref Ai[aslide[i] / 2]); + } + else if (aslide[i] < 0) + { + ge_p1p1_to_p3(out u, ref t); + ge_sub(out t, ref u, ref Ai[(-aslide[i]) / 2]); + } + + if (bslide[i] > 0) + { + ge_p1p1_to_p3(out u, ref t); + ge_madd(out t, ref u, ref Bi[bslide[i] / 2]); + } + else if (bslide[i] < 0) + { + ge_p1p1_to_p3(out u, ref t); + ge_msub(out t, ref u, ref Bi[(-bslide[i]) / 2]); + } + + ge_p1p1_to_p2(out r, ref t); + } + } + + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_frombytes.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_frombytes.cs new file mode 100644 index 000000000..38bfcfeb7 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_frombytes.cs @@ -0,0 +1,54 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class GroupOperations + { + internal static int ge_frombytes_negate_vartime(out GroupElementP3 h, byte[] data, int offset) + { + FieldElement u; + FieldElement v; + FieldElement v3; + FieldElement vxx; + FieldElement check; + + FieldOperations.fe_frombytes(out h.Y, data, offset); + FieldOperations.fe_1(out h.Z); + FieldOperations.fe_sq(out u, ref h.Y); + FieldOperations.fe_mul(out v, ref u, ref LookupTables.d); + FieldOperations.fe_sub(out u, ref u, ref h.Z); /* u = y^2-1 */ + FieldOperations.fe_add(out v, ref v, ref h.Z); /* v = dy^2+1 */ + + FieldOperations.fe_sq(out v3, ref v); + FieldOperations.fe_mul(out v3, ref v3, ref v); /* v3 = v^3 */ + FieldOperations.fe_sq(out h.X, ref v3); + FieldOperations.fe_mul(out h.X, ref h.X, ref v); + FieldOperations.fe_mul(out h.X, ref h.X, ref u); /* x = uv^7 */ + + FieldOperations.fe_pow22523(out h.X, ref h.X); /* x = (uv^7)^((q-5)/8) */ + FieldOperations.fe_mul(out h.X, ref h.X, ref v3); + FieldOperations.fe_mul(out h.X, ref h.X, ref u); /* x = uv^3(uv^7)^((q-5)/8) */ + + FieldOperations.fe_sq(out vxx, ref h.X); + FieldOperations.fe_mul(out vxx, ref vxx, ref v); + FieldOperations.fe_sub(out check, ref vxx, ref u); /* vx^2-u */ + if (FieldOperations.fe_isnonzero(ref check) != 0) + { + FieldOperations.fe_add(out check, ref vxx, ref u); /* vx^2+u */ + if (FieldOperations.fe_isnonzero(ref check) != 0) + { + h = default(GroupElementP3); + return -1; + } + FieldOperations.fe_mul(out h.X, ref h.X, ref LookupTables.sqrtm1); + } + + if (FieldOperations.fe_isnegative(ref h.X) == (data[offset + 31] >> 7)) + FieldOperations.fe_neg(out h.X, ref h.X); + + FieldOperations.fe_mul(out h.T, ref h.X, ref h.Y); + return 0; + } + + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs new file mode 100644 index 000000000..8832d3e90 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs @@ -0,0 +1,105 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class GroupOperations + { + /* + r = p + q + */ + internal static void ge_madd(out GroupElementP1P1 r, ref GroupElementP3 p, ref GroupElementPreComp q) + { + FieldElement t0; + + /* qhasm: enter ge_madd */ + + /* qhasm: fe X1 */ + + /* qhasm: fe Y1 */ + + /* qhasm: fe Z1 */ + + /* qhasm: fe T1 */ + + /* qhasm: fe ypx2 */ + + /* qhasm: fe ymx2 */ + + /* qhasm: fe xy2d2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Y3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe T3 */ + + /* qhasm: fe YpX1 */ + + /* qhasm: fe YmX1 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: YpX1 = Y1+X1 */ + /* asm 1: fe_add(>YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,XX=fe#1,XX=r.X,YY=fe#3,YY=r.Z,B=fe#4,B=r.T,A=fe#2,A=r.Y,AA=fe#5,AA=t0,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,X3=fe#1,X3=r.X,T3=fe#4,T3=r.T,>= 31; /* 1: yes; 0: no */ + return (byte)y; + } + + static byte negative(sbyte b) + { + ulong x = unchecked((ulong)(long)b); /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + x >>= 63; /* 1: yes; 0: no */ + return (byte)x; + } + + static void cmov(ref GroupElementPreComp t, ref GroupElementPreComp u, byte b) + { + FieldOperations.fe_cmov(ref t.yplusx, ref u.yplusx, b); + FieldOperations.fe_cmov(ref t.yminusx, ref u.yminusx, b); + FieldOperations.fe_cmov(ref t.xy2d, ref u.xy2d, b); + } + + static void select(out GroupElementPreComp t, int pos, sbyte b) + { + GroupElementPreComp minust; + byte bnegative = negative(b); + byte babs = (byte)(b - (((-bnegative) & b) << 1)); + + ge_precomp_0(out t); + var table = LookupTables.Base[pos]; + cmov(ref t, ref table[0], equal(babs, 1)); + cmov(ref t, ref table[1], equal(babs, 2)); + cmov(ref t, ref table[2], equal(babs, 3)); + cmov(ref t, ref table[3], equal(babs, 4)); + cmov(ref t, ref table[4], equal(babs, 5)); + cmov(ref t, ref table[5], equal(babs, 6)); + cmov(ref t, ref table[6], equal(babs, 7)); + cmov(ref t, ref table[7], equal(babs, 8)); + minust.yplusx = t.yminusx; + minust.yminusx = t.yplusx; + FieldOperations.fe_neg(out minust.xy2d, ref t.xy2d); + cmov(ref t, ref minust, bnegative); + } + + /* + h = a * B + where a = a[0]+256*a[1]+...+256^31 a[31] + B is the Ed25519 base point (x,4/5) with x positive. + + Preconditions: + a[31] <= 127 + */ + + internal static void ge_scalarmult_base(out GroupElementP3 h, byte[] a, int offset) + { + // todo: Perhaps remove this allocation + sbyte[] e = new sbyte[64]; + sbyte carry; + GroupElementP1P1 r; + GroupElementP2 s; + GroupElementPreComp t; + int i; + + for (i = 0; i < 32; ++i) + { + e[2 * i + 0] = (sbyte)((a[offset + i] >> 0) & 15); + e[2 * i + 1] = (sbyte)((a[offset + i] >> 4) & 15); + } + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + + carry = 0; + for (i = 0; i < 63; ++i) + { + e[i] += carry; + carry = (sbyte)(e[i] + 8); + carry >>= 4; + e[i] -= (sbyte)(carry << 4); + } + e[63] += carry; + /* each e[i] is between -8 and 8 */ + + ge_p3_0(out h); + for (i = 1; i < 64; i += 2) + { + select(out t, i / 2, e[i]); + ge_madd(out r, ref h, ref t); ge_p1p1_to_p3(out h, ref r); + } + + ge_p3_dbl(out r, ref h); ge_p1p1_to_p2(out s, ref r); + ge_p2_dbl(out r, ref s); ge_p1p1_to_p2(out s, ref r); + ge_p2_dbl(out r, ref s); ge_p1p1_to_p2(out s, ref r); + ge_p2_dbl(out r, ref s); ge_p1p1_to_p3(out h, ref r); + + for (i = 0; i < 64; i += 2) + { + select(out t, i / 2, e[i]); + ge_madd(out r, ref h, ref t); ge_p1p1_to_p3(out h, ref r); + } + } + + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_sub.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_sub.cs new file mode 100644 index 000000000..83cc5b278 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/ge_sub.cs @@ -0,0 +1,114 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class GroupOperations + { + /* + r = p - q + */ + + internal static void ge_sub(out GroupElementP1P1 r, ref GroupElementP3 p, ref GroupElementCached q) + { + FieldElement t0; + + /* qhasm: enter ge_sub */ + + /* qhasm: fe X1 */ + + /* qhasm: fe Y1 */ + + /* qhasm: fe Z1 */ + + /* qhasm: fe Z2 */ + + /* qhasm: fe T1 */ + + /* qhasm: fe ZZ */ + + /* qhasm: fe YpX2 */ + + /* qhasm: fe YmX2 */ + + /* qhasm: fe T2d2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Y3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe T3 */ + + /* qhasm: fe YpX1 */ + + /* qhasm: fe YmX1 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: YpX1 = Y1+X1 */ + /* asm 1: fe_add(>YpX1=fe#1,YpX1=r.X,YmX1=fe#2,YmX1=r.Y,A=fe#3,A=r.Z,B=fe#2,B=r.Y,C=fe#4,C=r.T,ZZ=fe#1,ZZ=r.X,D=fe#5,D=t0,X3=fe#1,X3=r.X,Y3=fe#2,Y3=r.Y,Z3=fe#3,Z3=r.Z,T3=fe#4,T3=r.T,> 5); + Int64 a2 = 2097151 & (load_3(a, 5) >> 2); + Int64 a3 = 2097151 & (load_4(a, 7) >> 7); + Int64 a4 = 2097151 & (load_4(a, 10) >> 4); + Int64 a5 = 2097151 & (load_3(a, 13) >> 1); + Int64 a6 = 2097151 & (load_4(a, 15) >> 6); + Int64 a7 = 2097151 & (load_3(a, 18) >> 3); + Int64 a8 = 2097151 & load_3(a, 21); + Int64 a9 = 2097151 & (load_4(a, 23) >> 5); + Int64 a10 = 2097151 & (load_3(a, 26) >> 2); + Int64 a11 = (load_4(a, 28) >> 7); + Int64 b0 = 2097151 & load_3(b, 0); + Int64 b1 = 2097151 & (load_4(b, 2) >> 5); + Int64 b2 = 2097151 & (load_3(b, 5) >> 2); + Int64 b3 = 2097151 & (load_4(b, 7) >> 7); + Int64 b4 = 2097151 & (load_4(b, 10) >> 4); + Int64 b5 = 2097151 & (load_3(b, 13) >> 1); + Int64 b6 = 2097151 & (load_4(b, 15) >> 6); + Int64 b7 = 2097151 & (load_3(b, 18) >> 3); + Int64 b8 = 2097151 & load_3(b, 21); + Int64 b9 = 2097151 & (load_4(b, 23) >> 5); + Int64 b10 = 2097151 & (load_3(b, 26) >> 2); + Int64 b11 = (load_4(b, 28) >> 7); + Int64 c0 = 2097151 & load_3(c, 0); + Int64 c1 = 2097151 & (load_4(c, 2) >> 5); + Int64 c2 = 2097151 & (load_3(c, 5) >> 2); + Int64 c3 = 2097151 & (load_4(c, 7) >> 7); + Int64 c4 = 2097151 & (load_4(c, 10) >> 4); + Int64 c5 = 2097151 & (load_3(c, 13) >> 1); + Int64 c6 = 2097151 & (load_4(c, 15) >> 6); + Int64 c7 = 2097151 & (load_3(c, 18) >> 3); + Int64 c8 = 2097151 & load_3(c, 21); + Int64 c9 = 2097151 & (load_4(c, 23) >> 5); + Int64 c10 = 2097151 & (load_3(c, 26) >> 2); + Int64 c11 = (load_4(c, 28) >> 7); + Int64 s0; + Int64 s1; + Int64 s2; + Int64 s3; + Int64 s4; + Int64 s5; + Int64 s6; + Int64 s7; + Int64 s8; + Int64 s9; + Int64 s10; + Int64 s11; + Int64 s12; + Int64 s13; + Int64 s14; + Int64 s15; + Int64 s16; + Int64 s17; + Int64 s18; + Int64 s19; + Int64 s20; + Int64 s21; + Int64 s22; + Int64 s23; + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + Int64 carry10; + Int64 carry11; + Int64 carry12; + Int64 carry13; + Int64 carry14; + Int64 carry15; + Int64 carry16; + Int64 carry17; + Int64 carry18; + Int64 carry19; + Int64 carry20; + Int64 carry21; + Int64 carry22; + + s0 = c0 + a0 * b0; + s1 = c1 + a0 * b1 + a1 * b0; + s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; + s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; + s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; + s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; + s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; + s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 + a7 * b1 + a8 * b0; + s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; + s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; + s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; + s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; + s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; + s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; + s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; + s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; + s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; + s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; + s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; + s20 = a9 * b11 + a10 * b10 + a11 * b9; + s21 = a10 * b11 + a11 * b10; + s22 = a11 * b11; + s23 = 0; + + carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + carry18 = (s18 + (1 << 20)) >> 21; s19 += carry18; s18 -= carry18 << 21; + carry20 = (s20 + (1 << 20)) >> 21; s21 += carry20; s20 -= carry20 << 21; + carry22 = (s22 + (1 << 20)) >> 21; s23 += carry22; s22 -= carry22 << 21; + + carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + carry17 = (s17 + (1 << 20)) >> 21; s18 += carry17; s17 -= carry17 << 21; + carry19 = (s19 + (1 << 20)) >> 21; s20 += carry19; s19 -= carry19 << 21; + carry21 = (s21 + (1 << 20)) >> 21; s22 += carry21; s21 -= carry21 << 21; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + + carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + unchecked + { + s[0] = (byte)(s0 >> 0); + s[1] = (byte)(s0 >> 8); + s[2] = (byte)((s0 >> 16) | (s1 << 5)); + s[3] = (byte)(s1 >> 3); + s[4] = (byte)(s1 >> 11); + s[5] = (byte)((s1 >> 19) | (s2 << 2)); + s[6] = (byte)(s2 >> 6); + s[7] = (byte)((s2 >> 14) | (s3 << 7)); + s[8] = (byte)(s3 >> 1); + s[9] = (byte)(s3 >> 9); + s[10] = (byte)((s3 >> 17) | (s4 << 4)); + s[11] = (byte)(s4 >> 4); + s[12] = (byte)(s4 >> 12); + s[13] = (byte)((s4 >> 20) | (s5 << 1)); + s[14] = (byte)(s5 >> 7); + s[15] = (byte)((s5 >> 15) | (s6 << 6)); + s[16] = (byte)(s6 >> 2); + s[17] = (byte)(s6 >> 10); + s[18] = (byte)((s6 >> 18) | (s7 << 3)); + s[19] = (byte)(s7 >> 5); + s[20] = (byte)(s7 >> 13); + s[21] = (byte)(s8 >> 0); + s[22] = (byte)(s8 >> 8); + s[23] = (byte)((s8 >> 16) | (s9 << 5)); + s[24] = (byte)(s9 >> 3); + s[25] = (byte)(s9 >> 11); + s[26] = (byte)((s9 >> 19) | (s10 << 2)); + s[27] = (byte)(s10 >> 6); + s[28] = (byte)((s10 >> 14) | (s11 << 7)); + s[29] = (byte)(s11 >> 1); + s[30] = (byte)(s11 >> 9); + s[31] = (byte)(s11 >> 17); + } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sc_reduce.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sc_reduce.cs new file mode 100644 index 000000000..e310331fb --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/sc_reduce.cs @@ -0,0 +1,263 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static partial class ScalarOperations + { + /* + Input: + s[0]+256*s[1]+...+256^63*s[63] = s + + Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. + */ + + internal static void sc_reduce(byte[] s) + { + Int64 s0 = 2097151 & load_3(s, 0); + Int64 s1 = 2097151 & (load_4(s, 2) >> 5); + Int64 s2 = 2097151 & (load_3(s, 5) >> 2); + Int64 s3 = 2097151 & (load_4(s, 7) >> 7); + Int64 s4 = 2097151 & (load_4(s, 10) >> 4); + Int64 s5 = 2097151 & (load_3(s, 13) >> 1); + Int64 s6 = 2097151 & (load_4(s, 15) >> 6); + Int64 s7 = 2097151 & (load_3(s, 18) >> 3); + Int64 s8 = 2097151 & load_3(s, 21); + Int64 s9 = 2097151 & (load_4(s, 23) >> 5); + Int64 s10 = 2097151 & (load_3(s, 26) >> 2); + Int64 s11 = 2097151 & (load_4(s, 28) >> 7); + Int64 s12 = 2097151 & (load_4(s, 31) >> 4); + Int64 s13 = 2097151 & (load_3(s, 34) >> 1); + Int64 s14 = 2097151 & (load_4(s, 36) >> 6); + Int64 s15 = 2097151 & (load_3(s, 39) >> 3); + Int64 s16 = 2097151 & load_3(s, 42); + Int64 s17 = 2097151 & (load_4(s, 44) >> 5); + Int64 s18 = 2097151 & (load_3(s, 47) >> 2); + Int64 s19 = 2097151 & (load_4(s, 49) >> 7); + Int64 s20 = 2097151 & (load_4(s, 52) >> 4); + Int64 s21 = 2097151 & (load_3(s, 55) >> 1); + Int64 s22 = 2097151 & (load_4(s, 57) >> 6); + Int64 s23 = (load_4(s, 60) >> 3); + Int64 carry0; + Int64 carry1; + Int64 carry2; + Int64 carry3; + Int64 carry4; + Int64 carry5; + Int64 carry6; + Int64 carry7; + Int64 carry8; + Int64 carry9; + Int64 carry10; + Int64 carry11; + Int64 carry12; + Int64 carry13; + Int64 carry14; + Int64 carry15; + Int64 carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + s23 = 0; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + s22 = 0; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + s21 = 0; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + s20 = 0; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + s19 = 0; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + s18 = 0; + + carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + carry12 = (s12 + (1 << 20)) >> 21; s13 += carry12; s12 -= carry12 << 21; + carry14 = (s14 + (1 << 20)) >> 21; s15 += carry14; s14 -= carry14 << 21; + carry16 = (s16 + (1 << 20)) >> 21; s17 += carry16; s16 -= carry16 << 21; + + carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + carry13 = (s13 + (1 << 20)) >> 21; s14 += carry13; s13 -= carry13 << 21; + carry15 = (s15 + (1 << 20)) >> 21; s16 += carry15; s15 -= carry15 << 21; + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + s17 = 0; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + s16 = 0; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + s15 = 0; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + s14 = 0; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + s13 = 0; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (1 << 20)) >> 21; s1 += carry0; s0 -= carry0 << 21; + carry2 = (s2 + (1 << 20)) >> 21; s3 += carry2; s2 -= carry2 << 21; + carry4 = (s4 + (1 << 20)) >> 21; s5 += carry4; s4 -= carry4 << 21; + carry6 = (s6 + (1 << 20)) >> 21; s7 += carry6; s6 -= carry6 << 21; + carry8 = (s8 + (1 << 20)) >> 21; s9 += carry8; s8 -= carry8 << 21; + carry10 = (s10 + (1 << 20)) >> 21; s11 += carry10; s10 -= carry10 << 21; + + carry1 = (s1 + (1 << 20)) >> 21; s2 += carry1; s1 -= carry1 << 21; + carry3 = (s3 + (1 << 20)) >> 21; s4 += carry3; s3 -= carry3 << 21; + carry5 = (s5 + (1 << 20)) >> 21; s6 += carry5; s5 -= carry5 << 21; + carry7 = (s7 + (1 << 20)) >> 21; s8 += carry7; s7 -= carry7 << 21; + carry9 = (s9 + (1 << 20)) >> 21; s10 += carry9; s9 -= carry9 << 21; + carry11 = (s11 + (1 << 20)) >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; + carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; + carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21; + carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21; + carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21; + carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21; + carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21; + carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21; + carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21; + carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21; + carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21; + + unchecked + { + s[0] = (byte)(s0 >> 0); + s[1] = (byte)(s0 >> 8); + s[2] = (byte)((s0 >> 16) | (s1 << 5)); + s[3] = (byte)(s1 >> 3); + s[4] = (byte)(s1 >> 11); + s[5] = (byte)((s1 >> 19) | (s2 << 2)); + s[6] = (byte)(s2 >> 6); + s[7] = (byte)((s2 >> 14) | (s3 << 7)); + s[8] = (byte)(s3 >> 1); + s[9] = (byte)(s3 >> 9); + s[10] = (byte)((s3 >> 17) | (s4 << 4)); + s[11] = (byte)(s4 >> 4); + s[12] = (byte)(s4 >> 12); + s[13] = (byte)((s4 >> 20) | (s5 << 1)); + s[14] = (byte)(s5 >> 7); + s[15] = (byte)((s5 >> 15) | (s6 << 6)); + s[16] = (byte)(s6 >> 2); + s[17] = (byte)(s6 >> 10); + s[18] = (byte)((s6 >> 18) | (s7 << 3)); + s[19] = (byte)(s7 >> 5); + s[20] = (byte)(s7 >> 13); + s[21] = (byte)(s8 >> 0); + s[22] = (byte)(s8 >> 8); + s[23] = (byte)((s8 >> 16) | (s9 << 5)); + s[24] = (byte)(s9 >> 3); + s[25] = (byte)(s9 >> 11); + s[26] = (byte)((s9 >> 19) | (s10 << 2)); + s[27] = (byte)(s10 >> 6); + s[28] = (byte)((s10 >> 14) | (s11 << 7)); + s[29] = (byte)(s11 >> 1); + s[30] = (byte)(s11 >> 9); + s[31] = (byte)(s11 >> 17); + } + } + + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/scalarmult.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/scalarmult.cs new file mode 100644 index 000000000..5c439da7f --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Ed25519Ref10/scalarmult.cs @@ -0,0 +1,205 @@ +using System; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10 +{ + internal static class MontgomeryOperations + { + internal static void scalarmult( + byte[] q, int qoffset, + byte[] n, int noffset, + byte[] p, int poffset) + { + FieldElement p0; + FieldElement q0; + FieldOperations.fe_frombytes2(out p0, p, poffset); + scalarmult(out q0, n, noffset, ref p0); + FieldOperations.fe_tobytes(q, qoffset, ref q0); + } + + internal static void scalarmult( + out FieldElement q, + byte[] n, int noffset, + ref FieldElement p) + { + byte[] e = new byte[32];//ToDo: remove allocation + UInt32 i; + FieldElement x1; + FieldElement x2; + FieldElement z2; + FieldElement x3; + FieldElement z3; + FieldElement tmp0; + FieldElement tmp1; + int pos; + UInt32 swap; + UInt32 b; + + for (i = 0; i < 32; ++i) + e[i] = n[noffset + i]; + ScalarOperations.sc_clamp(e, 0); + x1 = p; + FieldOperations.fe_1(out x2); + FieldOperations.fe_0(out z2); + x3 = x1; + FieldOperations.fe_1(out z3); + + swap = 0; + for (pos = 254; pos >= 0; --pos) + { + b = (uint)(e[pos / 8] >> (pos & 7)); + b &= 1; + swap ^= b; + FieldOperations.fe_cswap(ref x2, ref x3, swap); + FieldOperations.fe_cswap(ref z2, ref z3, swap); + swap = b; + /* qhasm: fe X2 */ + + /* qhasm: fe Z2 */ + + /* qhasm: fe X3 */ + + /* qhasm: fe Z3 */ + + /* qhasm: fe X4 */ + + /* qhasm: fe Z4 */ + + /* qhasm: fe X5 */ + + /* qhasm: fe Z5 */ + + /* qhasm: fe A */ + + /* qhasm: fe B */ + + /* qhasm: fe C */ + + /* qhasm: fe D */ + + /* qhasm: fe E */ + + /* qhasm: fe AA */ + + /* qhasm: fe BB */ + + /* qhasm: fe DA */ + + /* qhasm: fe CB */ + + /* qhasm: fe t0 */ + + /* qhasm: fe t1 */ + + /* qhasm: fe t2 */ + + /* qhasm: fe t3 */ + + /* qhasm: fe t4 */ + + /* qhasm: enter ladder */ + + /* qhasm: D = X3-Z3 */ + /* asm 1: fe_sub(>D=fe#5,D=tmp0,B=fe#6,B=tmp1,A=fe#1,A=x2,C=fe#2,C=z2,DA=fe#4,DA=z3,CB=fe#2,CB=z2,BB=fe#5,BB=tmp0,AA=fe#6,AA=tmp1,t0=fe#3,t0=x3,t1=fe#2,t1=z2,X4=fe#1,X4=x2,E=fe#6,E=tmp1,t2=fe#2,t2=z2,t3=fe#4,t3=z3,X5=fe#3,X5=x3,t4=fe#5,t4=tmp0,Z5=fe#4,x1,Z5=z3,x1,Z4=fe#2,Z4=z2, key) + { + UInt32 t0, t1, t2, t3; + UInt32 h0, h1, h2, h3, h4; + UInt32 r0, r1, r2, r3, r4; + UInt32 s1, s2, s3, s4; + UInt32 b, nb; + int j; + UInt64 tt0, tt1, tt2, tt3, tt4; + UInt64 f0, f1, f2, f3; + UInt32 g0, g1, g2, g3, g4; + UInt64 c; + + /* clamp key */ + t0 = key.x0; + t1 = key.x1; + t2 = key.x2; + t3 = key.x3; + + /* precompute multipliers */ + r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6; + r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12; + r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18; + r3 = t2 & 0x3f03fff; t3 >>= 8; + r4 = t3 & 0x00fffff; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + /* init state */ + h0 = 0; + h1 = 0; + h2 = 0; + h3 = 0; + h4 = 0; + + /* full blocks */ + if (mLength < 16) + goto poly1305_donna_atmost15bytes; + + poly1305_donna_16bytes: + mStart += 16; + mLength -= 16; + + t0 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 16); + t1 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 12); + t2 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 8); + t3 = ByteIntegerConverter.LoadLittleEndian32(m, mStart - 4); + + //todo: looks like these can be simplified a bit + h0 += t0 & 0x3ffffff; + h1 += (uint)(((((UInt64)t1 << 32) | t0) >> 26) & 0x3ffffff); + h2 += (uint)(((((UInt64)t2 << 32) | t1) >> 20) & 0x3ffffff); + h3 += (uint)(((((UInt64)t3 << 32) | t2) >> 14) & 0x3ffffff); + h4 += (t3 >> 8) | (1 << 24); + + + poly1305_donna_mul: + tt0 = (ulong)h0 * r0 + (ulong)h1 * s4 + (ulong)h2 * s3 + (ulong)h3 * s2 + (ulong)h4 * s1; + tt1 = (ulong)h0 * r1 + (ulong)h1 * r0 + (ulong)h2 * s4 + (ulong)h3 * s3 + (ulong)h4 * s2; + tt2 = (ulong)h0 * r2 + (ulong)h1 * r1 + (ulong)h2 * r0 + (ulong)h3 * s4 + (ulong)h4 * s3; + tt3 = (ulong)h0 * r3 + (ulong)h1 * r2 + (ulong)h2 * r1 + (ulong)h3 * r0 + (ulong)h4 * s4; + tt4 = (ulong)h0 * r4 + (ulong)h1 * r3 + (ulong)h2 * r2 + (ulong)h3 * r1 + (ulong)h4 * r0; + + unchecked + { + h0 = (UInt32)tt0 & 0x3ffffff; c = (tt0 >> 26); + tt1 += c; h1 = (UInt32)tt1 & 0x3ffffff; b = (UInt32)(tt1 >> 26); + tt2 += b; h2 = (UInt32)tt2 & 0x3ffffff; b = (UInt32)(tt2 >> 26); + tt3 += b; h3 = (UInt32)tt3 & 0x3ffffff; b = (UInt32)(tt3 >> 26); + tt4 += b; h4 = (UInt32)tt4 & 0x3ffffff; b = (UInt32)(tt4 >> 26); + } + h0 += b * 5; + + if (mLength >= 16) + goto poly1305_donna_16bytes; + + /* final bytes */ + poly1305_donna_atmost15bytes: + if (mLength == 0) + goto poly1305_donna_finish; + + byte[] mp = new byte[16];//todo remove allocation + + for (j = 0; j < mLength; j++) + mp[j] = m[mStart + j]; + mp[j++] = 1; + for (; j < 16; j++) + mp[j] = 0; + mLength = 0; + + t0 = ByteIntegerConverter.LoadLittleEndian32(mp, 0); + t1 = ByteIntegerConverter.LoadLittleEndian32(mp, 4); + t2 = ByteIntegerConverter.LoadLittleEndian32(mp, 8); + t3 = ByteIntegerConverter.LoadLittleEndian32(mp, 12); + CryptoBytes.Wipe(mp); + + h0 += t0 & 0x3ffffff; + h1 += (uint)(((((UInt64)t1 << 32) | t0) >> 26) & 0x3ffffff); + h2 += (uint)(((((UInt64)t2 << 32) | t1) >> 20) & 0x3ffffff); + h3 += (uint)(((((UInt64)t3 << 32) | t2) >> 14) & 0x3ffffff); + h4 += t3 >> 8; + + goto poly1305_donna_mul; + + poly1305_donna_finish: + b = h0 >> 26; h0 = h0 & 0x3ffffff; + h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff; + h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff; + h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff; + h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff; + h0 += b * 5; + + g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff; + g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff; + g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff; + g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff; + g4 = unchecked(h4 + b - (1 << 26)); + + b = (g4 >> 31) - 1; + nb = ~b; + h0 = (h0 & nb) | (g0 & b); + h1 = (h1 & nb) | (g1 & b); + h2 = (h2 & nb) | (g2 & b); + h3 = (h3 & nb) | (g3 & b); + h4 = (h4 & nb) | (g4 & b); + + f0 = ((h0) | (h1 << 26)) + (UInt64)key.x4; + f1 = ((h1 >> 6) | (h2 << 20)) + (UInt64)key.x5; + f2 = ((h2 >> 12) | (h3 << 14)) + (UInt64)key.x6; + f3 = ((h3 >> 18) | (h4 << 8)) + (UInt64)key.x7; + + unchecked + { + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 0, (uint)f0); f1 += (f0 >> 32); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 4, (uint)f1); f2 += (f1 >> 32); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 8, (uint)f2); f3 += (f2 >> 32); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 12, (uint)f3); + } + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/Salsa20.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/Salsa20.cs new file mode 100644 index 000000000..4b74ad0c5 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/Salsa20.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Salsa +{ + internal class Salsa20 + { + public const uint SalsaConst0 = 0x61707865; + public const uint SalsaConst1 = 0x3320646e; + public const uint SalsaConst2 = 0x79622d32; + public const uint SalsaConst3 = 0x6b206574; + + internal static void HSalsa20(byte[] output, int outputOffset, byte[] key, int keyOffset, byte[] nonce, int nonceOffset) + { + Array16 state; + state.x0 = SalsaConst0; + state.x1 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 0); + state.x2 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 4); + state.x3 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 8); + state.x4 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 12); + state.x5 = SalsaConst1; + state.x6 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 0); + state.x7 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 4); + state.x8 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 8); + state.x9 = ByteIntegerConverter.LoadLittleEndian32(nonce, nonceOffset + 12); + state.x10 = SalsaConst2; + state.x11 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 16); + state.x12 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 20); + state.x13 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 24); + state.x14 = ByteIntegerConverter.LoadLittleEndian32(key, keyOffset + 28); + state.x15 = SalsaConst3; + + SalsaCore.HSalsa(out state, ref state, 20); + + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 0, state.x0); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 4, state.x5); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 8, state.x10); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 12, state.x15); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 16, state.x6); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 20, state.x7); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 24, state.x8); + ByteIntegerConverter.StoreLittleEndian32(output, outputOffset + 28, state.x9); + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/SalsaCore.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/SalsaCore.cs new file mode 100644 index 000000000..e539d668f --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/SalsaCore.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal.Salsa +{ + internal static class SalsaCore + { + internal static void HSalsa(out Array16 output, ref Array16 input, int rounds) + { + InternalAssert.Assert(rounds % 2 == 0, "Number of salsa rounds must be even"); + + int doubleRounds = rounds / 2; + + UInt32 x0 = input.x0; + UInt32 x1 = input.x1; + UInt32 x2 = input.x2; + UInt32 x3 = input.x3; + UInt32 x4 = input.x4; + UInt32 x5 = input.x5; + UInt32 x6 = input.x6; + UInt32 x7 = input.x7; + UInt32 x8 = input.x8; + UInt32 x9 = input.x9; + UInt32 x10 = input.x10; + UInt32 x11 = input.x11; + UInt32 x12 = input.x12; + UInt32 x13 = input.x13; + UInt32 x14 = input.x14; + UInt32 x15 = input.x15; + + unchecked + { + for (int i = 0; i < doubleRounds; i++) + { + UInt32 y; + + // row 0 + y = x0 + x12; + x4 ^= (y << 7) | (y >> (32 - 7)); + y = x4 + x0; + x8 ^= (y << 9) | (y >> (32 - 9)); + y = x8 + x4; + x12 ^= (y << 13) | (y >> (32 - 13)); + y = x12 + x8; + x0 ^= (y << 18) | (y >> (32 - 18)); + + // row 1 + y = x5 + x1; + x9 ^= (y << 7) | (y >> (32 - 7)); + y = x9 + x5; + x13 ^= (y << 9) | (y >> (32 - 9)); + y = x13 + x9; + x1 ^= (y << 13) | (y >> (32 - 13)); + y = x1 + x13; + x5 ^= (y << 18) | (y >> (32 - 18)); + + // row 2 + y = x10 + x6; + x14 ^= (y << 7) | (y >> (32 - 7)); + y = x14 + x10; + x2 ^= (y << 9) | (y >> (32 - 9)); + y = x2 + x14; + x6 ^= (y << 13) | (y >> (32 - 13)); + y = x6 + x2; + x10 ^= (y << 18) | (y >> (32 - 18)); + + // row 3 + y = x15 + x11; + x3 ^= (y << 7) | (y >> (32 - 7)); + y = x3 + x15; + x7 ^= (y << 9) | (y >> (32 - 9)); + y = x7 + x3; + x11 ^= (y << 13) | (y >> (32 - 13)); + y = x11 + x7; + x15 ^= (y << 18) | (y >> (32 - 18)); + + // column 0 + y = x0 + x3; + x1 ^= (y << 7) | (y >> (32 - 7)); + y = x1 + x0; + x2 ^= (y << 9) | (y >> (32 - 9)); + y = x2 + x1; + x3 ^= (y << 13) | (y >> (32 - 13)); + y = x3 + x2; + x0 ^= (y << 18) | (y >> (32 - 18)); + + // column 1 + y = x5 + x4; + x6 ^= (y << 7) | (y >> (32 - 7)); + y = x6 + x5; + x7 ^= (y << 9) | (y >> (32 - 9)); + y = x7 + x6; + x4 ^= (y << 13) | (y >> (32 - 13)); + y = x4 + x7; + x5 ^= (y << 18) | (y >> (32 - 18)); + + // column 2 + y = x10 + x9; + x11 ^= (y << 7) | (y >> (32 - 7)); + y = x11 + x10; + x8 ^= (y << 9) | (y >> (32 - 9)); + y = x8 + x11; + x9 ^= (y << 13) | (y >> (32 - 13)); + y = x9 + x8; + x10 ^= (y << 18) | (y >> (32 - 18)); + + // column 3 + y = x15 + x14; + x12 ^= (y << 7) | (y >> (32 - 7)); + y = x12 + x15; + x13 ^= (y << 9) | (y >> (32 - 9)); + y = x13 + x12; + x14 ^= (y << 13) | (y >> (32 - 13)); + y = x14 + x13; + x15 ^= (y << 18) | (y >> (32 - 18)); + } + } + + output.x0 = x0; + output.x1 = x1; + output.x2 = x2; + output.x3 = x3; + output.x4 = x4; + output.x5 = x5; + output.x6 = x6; + output.x7 = x7; + output.x8 = x8; + output.x9 = x9; + output.x10 = x10; + output.x11 = x11; + output.x12 = x12; + output.x13 = x13; + output.x14 = x14; + output.x15 = x15; + } + + internal static void Salsa(out Array16 output, ref Array16 input, int rounds) + { + Array16 temp; + HSalsa(out temp, ref input, rounds); + unchecked + { + output.x0 = temp.x0 + input.x0; + output.x1 = temp.x1 + input.x1; + output.x2 = temp.x2 + input.x2; + output.x3 = temp.x3 + input.x3; + output.x4 = temp.x4 + input.x4; + output.x5 = temp.x5 + input.x5; + output.x6 = temp.x6 + input.x6; + output.x7 = temp.x7 + input.x7; + output.x8 = temp.x8 + input.x8; + output.x9 = temp.x9 + input.x9; + output.x10 = temp.x10 + input.x10; + output.x11 = temp.x11 + input.x11; + output.x12 = temp.x12 + input.x12; + output.x13 = temp.x13 + input.x13; + output.x14 = temp.x14 + input.x14; + output.x15 = temp.x15 + input.x15; + } + } + + /*internal static void SalsaCore(int[] output, int outputOffset, int[] input, int inputOffset, int rounds) + { + if (rounds % 2 != 0) + throw new ArgumentException("rounds must be even"); + } + + +static void store_littleendian(unsigned char *x,uint32 u) +{ + x[0] = u; u >>= 8; + x[1] = u; u >>= 8; + x[2] = u; u >>= 8; + x[3] = u; +} + + internal static void HSalsaCore(int[] output, int outputOffset, int[] input, int inputOffset, int rounds) + { + if (rounds % 2 != 0) + throw new ArgumentException("rounds must be even"); + static uint32 rotate(uint32 u,int c) +{ + return (u << c) | (u >> (32 - c)); +} + + + +int crypto_core( + unsigned char *out, + const unsigned char *in, + const unsigned char *k, + const unsigned char *c +) +{ + uint32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int i; + + x0 = load_littleendian(c + 0); + x1 = load_littleendian(k + 0); + x2 = load_littleendian(k + 4); + x3 = load_littleendian(k + 8); + x4 = load_littleendian(k + 12); + x5 = load_littleendian(c + 4); + x6 = load_littleendian(in + 0); + x7 = load_littleendian(in + 4); + x8 = load_littleendian(in + 8); + x9 = load_littleendian(in + 12); + x10 = load_littleendian(c + 8); + x11 = load_littleendian(k + 16); + x12 = load_littleendian(k + 20); + x13 = load_littleendian(k + 24); + x14 = load_littleendian(k + 28); + x15 = load_littleendian(c + 12); + + for (i = ROUNDS;i > 0;i -= 2) { + x4 ^= rotate( x0+x12, 7); + x8 ^= rotate( x4+ x0, 9); + x12 ^= rotate( x8+ x4,13); + x0 ^= rotate(x12+ x8,18); + x9 ^= rotate( x5+ x1, 7); + x13 ^= rotate( x9+ x5, 9); + x1 ^= rotate(x13+ x9,13); + x5 ^= rotate( x1+x13,18); + x14 ^= rotate(x10+ x6, 7); + x2 ^= rotate(x14+x10, 9); + x6 ^= rotate( x2+x14,13); + x10 ^= rotate( x6+ x2,18); + x3 ^= rotate(x15+x11, 7); + x7 ^= rotate( x3+x15, 9); + x11 ^= rotate( x7+ x3,13); + x15 ^= rotate(x11+ x7,18); + x1 ^= rotate( x0+ x3, 7); + x2 ^= rotate( x1+ x0, 9); + x3 ^= rotate( x2+ x1,13); + x0 ^= rotate( x3+ x2,18); + x6 ^= rotate( x5+ x4, 7); + x7 ^= rotate( x6+ x5, 9); + x4 ^= rotate( x7+ x6,13); + x5 ^= rotate( x4+ x7,18); + x11 ^= rotate(x10+ x9, 7); + x8 ^= rotate(x11+x10, 9); + x9 ^= rotate( x8+x11,13); + x10 ^= rotate( x9+ x8,18); + x12 ^= rotate(x15+x14, 7); + x13 ^= rotate(x12+x15, 9); + x14 ^= rotate(x13+x12,13); + x15 ^= rotate(x14+x13,18); + } + + store_littleendian(out + 0,x0); + store_littleendian(out + 4,x5); + store_littleendian(out + 8,x10); + store_littleendian(out + 12,x15); + store_littleendian(out + 16,x6); + store_littleendian(out + 20,x7); + store_littleendian(out + 24,x8); + store_littleendian(out + 28,x9); + + return 0; +}*/ + + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/replace regex.txt b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/replace regex.txt new file mode 100644 index 000000000..f4856899e --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Salsa/replace regex.txt @@ -0,0 +1,2 @@ +x(\d+) ^= rotate\((.+), (\d+)\); +y = \2;\r\nx\1 ^= (y << \3) | (y >> (32 - \3)); \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Sha512Internal.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Sha512Internal.cs new file mode 100644 index 000000000..0f05d5ed9 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Internal/Sha512Internal.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections.Generic; + +namespace Renci.SshNet.Security.Chaos.NaCl.Internal +{ + internal static class Sha512Internal + { + private static readonly UInt64[] K = new UInt64[] + { + 0x428a2f98d728ae22,0x7137449123ef65cd,0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc, + 0x3956c25bf348b538,0x59f111f1b605d019,0x923f82a4af194f9b,0xab1c5ed5da6d8118, + 0xd807aa98a3030242,0x12835b0145706fbe,0x243185be4ee4b28c,0x550c7dc3d5ffb4e2, + 0x72be5d74f27b896f,0x80deb1fe3b1696b1,0x9bdc06a725c71235,0xc19bf174cf692694, + 0xe49b69c19ef14ad2,0xefbe4786384f25e3,0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65, + 0x2de92c6f592b0275,0x4a7484aa6ea6e483,0x5cb0a9dcbd41fbd4,0x76f988da831153b5, + 0x983e5152ee66dfab,0xa831c66d2db43210,0xb00327c898fb213f,0xbf597fc7beef0ee4, + 0xc6e00bf33da88fc2,0xd5a79147930aa725,0x06ca6351e003826f,0x142929670a0e6e70, + 0x27b70a8546d22ffc,0x2e1b21385c26c926,0x4d2c6dfc5ac42aed,0x53380d139d95b3df, + 0x650a73548baf63de,0x766a0abb3c77b2a8,0x81c2c92e47edaee6,0x92722c851482353b, + 0xa2bfe8a14cf10364,0xa81a664bbc423001,0xc24b8b70d0f89791,0xc76c51a30654be30, + 0xd192e819d6ef5218,0xd69906245565a910,0xf40e35855771202a,0x106aa07032bbd1b8, + 0x19a4c116b8d2d0c8,0x1e376c085141ab53,0x2748774cdf8eeb99,0x34b0bcb5e19b48a8, + 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb,0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3, + 0x748f82ee5defb2fc,0x78a5636f43172f60,0x84c87814a1f0ab72,0x8cc702081a6439ec, + 0x90befffa23631e28,0xa4506cebde82bde9,0xbef9a3f7b2c67915,0xc67178f2e372532b, + 0xca273eceea26619c,0xd186b8c721c0c207,0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178, + 0x06f067aa72176fba,0x0a637dc5a2c898a6,0x113f9804bef90dae,0x1b710b35131c471b, + 0x28db77f523047d84,0x32caab7b40c72493,0x3c9ebe0a15c9bebc,0x431d67c49c100d4c, + 0x4cc5d4becb3e42b6,0x597f299cfc657e2a,0x5fcb6fab3ad6faec,0x6c44198c4a475817 + }; + + internal static void Sha512Init(out Array8 state) + { + state.x0 = 0x6a09e667f3bcc908; + state.x1 = 0xbb67ae8584caa73b; + state.x2 = 0x3c6ef372fe94f82b; + state.x3 = 0xa54ff53a5f1d36f1; + state.x4 = 0x510e527fade682d1; + state.x5 = 0x9b05688c2b3e6c1f; + state.x6 = 0x1f83d9abfb41bd6b; + state.x7 = 0x5be0cd19137e2179; + } + + internal static void Core(out Array8 outputState, ref Array8 inputState, ref Array16 input) + { + unchecked + { + UInt64 a = inputState.x0; + UInt64 b = inputState.x1; + UInt64 c = inputState.x2; + UInt64 d = inputState.x3; + UInt64 e = inputState.x4; + UInt64 f = inputState.x5; + UInt64 g = inputState.x6; + UInt64 h = inputState.x7; + + UInt64 w0 = input.x0; + UInt64 w1 = input.x1; + UInt64 w2 = input.x2; + UInt64 w3 = input.x3; + UInt64 w4 = input.x4; + UInt64 w5 = input.x5; + UInt64 w6 = input.x6; + UInt64 w7 = input.x7; + UInt64 w8 = input.x8; + UInt64 w9 = input.x9; + UInt64 w10 = input.x10; + UInt64 w11 = input.x11; + UInt64 w12 = input.x12; + UInt64 w13 = input.x13; + UInt64 w14 = input.x14; + UInt64 w15 = input.x15; + + int t = 0; + while (true) + { + ulong t1, t2; + + {//0 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w0; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//1 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w1; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//2 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w2; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//3 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w3; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//4 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w4; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//5 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w5; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//6 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w6; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//7 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w7; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//8 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w8; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//9 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w9; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//10 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w10; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//11 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w11; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//12 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w12; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//13 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w13; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//14 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w14; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + {//15 + t1 = h + + ((e >> 14) ^ (e << (64 - 14)) ^ (e >> 18) ^ (e << (64 - 18)) ^ (e >> 41) ^ (e << (64 - 41))) + + //Sigma1(e) + ((e & f) ^ (~e & g)) + //Ch(e,f,g) + K[t] + w15; + t2 = ((a >> 28) ^ (a << (64 - 28)) ^ (a >> 34) ^ (a << (64 - 34)) ^ (a >> 39) ^ (a << (64 - 39))) + + //Sigma0(a) + ((a & b) ^ (a & c) ^ (b & c)); //Maj(a,b,c) + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + t++; + } + if (t == 80) + break; + + w0 += ((w14 >> 19) ^ (w14 << (64 - 19)) ^ (w14 >> 61) ^ (w14 << (64 - 61)) ^ (w14 >> 6)) + + w9 + + ((w1 >> 1) ^ (w1 << (64 - 1)) ^ (w1 >> 8) ^ (w1 << (64 - 8)) ^ (w1 >> 7)); + w1 += ((w15 >> 19) ^ (w15 << (64 - 19)) ^ (w15 >> 61) ^ (w15 << (64 - 61)) ^ (w15 >> 6)) + + w10 + + ((w2 >> 1) ^ (w2 << (64 - 1)) ^ (w2 >> 8) ^ (w2 << (64 - 8)) ^ (w2 >> 7)); + w2 += ((w0 >> 19) ^ (w0 << (64 - 19)) ^ (w0 >> 61) ^ (w0 << (64 - 61)) ^ (w0 >> 6)) + + w11 + + ((w3 >> 1) ^ (w3 << (64 - 1)) ^ (w3 >> 8) ^ (w3 << (64 - 8)) ^ (w3 >> 7)); + w3 += ((w1 >> 19) ^ (w1 << (64 - 19)) ^ (w1 >> 61) ^ (w1 << (64 - 61)) ^ (w1 >> 6)) + + w12 + + ((w4 >> 1) ^ (w4 << (64 - 1)) ^ (w4 >> 8) ^ (w4 << (64 - 8)) ^ (w4 >> 7)); + w4 += ((w2 >> 19) ^ (w2 << (64 - 19)) ^ (w2 >> 61) ^ (w2 << (64 - 61)) ^ (w2 >> 6)) + + w13 + + ((w5 >> 1) ^ (w5 << (64 - 1)) ^ (w5 >> 8) ^ (w5 << (64 - 8)) ^ (w5 >> 7)); + w5 += ((w3 >> 19) ^ (w3 << (64 - 19)) ^ (w3 >> 61) ^ (w3 << (64 - 61)) ^ (w3 >> 6)) + + w14 + + ((w6 >> 1) ^ (w6 << (64 - 1)) ^ (w6 >> 8) ^ (w6 << (64 - 8)) ^ (w6 >> 7)); + w6 += ((w4 >> 19) ^ (w4 << (64 - 19)) ^ (w4 >> 61) ^ (w4 << (64 - 61)) ^ (w4 >> 6)) + + w15 + + ((w7 >> 1) ^ (w7 << (64 - 1)) ^ (w7 >> 8) ^ (w7 << (64 - 8)) ^ (w7 >> 7)); + w7 += ((w5 >> 19) ^ (w5 << (64 - 19)) ^ (w5 >> 61) ^ (w5 << (64 - 61)) ^ (w5 >> 6)) + + w0 + + ((w8 >> 1) ^ (w8 << (64 - 1)) ^ (w8 >> 8) ^ (w8 << (64 - 8)) ^ (w8 >> 7)); + w8 += ((w6 >> 19) ^ (w6 << (64 - 19)) ^ (w6 >> 61) ^ (w6 << (64 - 61)) ^ (w6 >> 6)) + + w1 + + ((w9 >> 1) ^ (w9 << (64 - 1)) ^ (w9 >> 8) ^ (w9 << (64 - 8)) ^ (w9 >> 7)); + w9 += ((w7 >> 19) ^ (w7 << (64 - 19)) ^ (w7 >> 61) ^ (w7 << (64 - 61)) ^ (w7 >> 6)) + + w2 + + ((w10 >> 1) ^ (w10 << (64 - 1)) ^ (w10 >> 8) ^ (w10 << (64 - 8)) ^ (w10 >> 7)); + w10 += ((w8 >> 19) ^ (w8 << (64 - 19)) ^ (w8 >> 61) ^ (w8 << (64 - 61)) ^ (w8 >> 6)) + + w3 + + ((w11 >> 1) ^ (w11 << (64 - 1)) ^ (w11 >> 8) ^ (w11 << (64 - 8)) ^ (w11 >> 7)); + w11 += ((w9 >> 19) ^ (w9 << (64 - 19)) ^ (w9 >> 61) ^ (w9 << (64 - 61)) ^ (w9 >> 6)) + + w4 + + ((w12 >> 1) ^ (w12 << (64 - 1)) ^ (w12 >> 8) ^ (w12 << (64 - 8)) ^ (w12 >> 7)); + w12 += ((w10 >> 19) ^ (w10 << (64 - 19)) ^ (w10 >> 61) ^ (w10 << (64 - 61)) ^ (w10 >> 6)) + + w5 + + ((w13 >> 1) ^ (w13 << (64 - 1)) ^ (w13 >> 8) ^ (w13 << (64 - 8)) ^ (w13 >> 7)); + w13 += ((w11 >> 19) ^ (w11 << (64 - 19)) ^ (w11 >> 61) ^ (w11 << (64 - 61)) ^ (w11 >> 6)) + + w6 + + ((w14 >> 1) ^ (w14 << (64 - 1)) ^ (w14 >> 8) ^ (w14 << (64 - 8)) ^ (w14 >> 7)); + w14 += ((w12 >> 19) ^ (w12 << (64 - 19)) ^ (w12 >> 61) ^ (w12 << (64 - 61)) ^ (w12 >> 6)) + + w7 + + ((w15 >> 1) ^ (w15 << (64 - 1)) ^ (w15 >> 8) ^ (w15 << (64 - 8)) ^ (w15 >> 7)); + w15 += ((w13 >> 19) ^ (w13 << (64 - 19)) ^ (w13 >> 61) ^ (w13 << (64 - 61)) ^ (w13 >> 6)) + + w8 + + ((w0 >> 1) ^ (w0 << (64 - 1)) ^ (w0 >> 8) ^ (w0 << (64 - 8)) ^ (w0 >> 7)); + } + + outputState.x0 = inputState.x0 + a; + outputState.x1 = inputState.x1 + b; + outputState.x2 = inputState.x2 + c; + outputState.x3 = inputState.x3 + d; + outputState.x4 = inputState.x4 + e; + outputState.x5 = inputState.x5 + f; + outputState.x6 = inputState.x6 + g; + outputState.x7 = inputState.x7 + h; + } + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/License.txt b/src/Renci.SshNet/Security/Chaos.NaCl/License.txt new file mode 100644 index 000000000..fa608d142 --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/License.txt @@ -0,0 +1,23 @@ +Public domain + +C# port + code by Christian Winnerlein (CodesInChaos) + +Poly1305 in c + written by Andrew M. (floodyberry) + original license: MIT or PUBLIC DOMAIN + https://github.com/floodyberry/poly1305-donna/blob/master/poly1305-donna-unrolled.c + +Curve25519 and Ed25519 in c + written by Dan Bernstein (djb) + public domain + from Ref10 in SUPERCOP http://bench.cr.yp.to/supercop.html + +(H)Salsa20 in c + written by Dan Bernstein (djb) + public domain + from SUPERCOP http://bench.cr.yp.to/supercop.html + +SHA512 + written by Christian Winnerlein (CodesInChaos) + public domain + directly from the specification \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/MontgomeryCurve25519.cs b/src/Renci.SshNet/Security/Chaos.NaCl/MontgomeryCurve25519.cs new file mode 100644 index 000000000..2b99ce55c --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/MontgomeryCurve25519.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using Renci.SshNet.Security.Chaos.NaCl.Internal; +using Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10; +using Renci.SshNet.Security.Chaos.NaCl.Internal.Salsa; + +namespace Renci.SshNet.Security.Chaos.NaCl +{ + // This class is mainly for compatibility with NaCl's Curve25519 implementation + // If you don't need that compatibility, use Ed25519.KeyExchange + internal static class MontgomeryCurve25519 + { + internal static readonly int PublicKeySizeInBytes = 32; + internal static readonly int PrivateKeySizeInBytes = 32; + internal static readonly int SharedKeySizeInBytes = 32; + + internal static byte[] GetPublicKey(byte[] privateKey) + { + if (privateKey == null) + throw new ArgumentNullException("privateKey"); + if (privateKey.Length != PrivateKeySizeInBytes) + throw new ArgumentException("privateKey.Length must be 32"); + var publicKey = new byte[32]; + GetPublicKey(new ArraySegment(publicKey), new ArraySegment(privateKey)); + return publicKey; + } + + static readonly byte[] _basePoint = new byte[32] + { + 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 ,0, 0, 0, 0, 0, + 0, 0, 0 ,0, 0, 0, 0, 0, + 0, 0, 0 ,0, 0, 0, 0, 0 + }; + + internal static void GetPublicKey(ArraySegment publicKey, ArraySegment privateKey) + { + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (privateKey.Array == null) + throw new ArgumentNullException("privateKey.Array"); + if (publicKey.Count != PublicKeySizeInBytes) + throw new ArgumentException("privateKey.Count must be 32"); + if (privateKey.Count != PrivateKeySizeInBytes) + throw new ArgumentException("privateKey.Count must be 32"); + + // hack: abusing publicKey as temporary storage + // todo: remove hack + for (int i = 0; i < 32; i++) + { + publicKey.Array[publicKey.Offset + i] = privateKey.Array[privateKey.Offset + i]; + } + ScalarOperations.sc_clamp(publicKey.Array, publicKey.Offset); + + GroupElementP3 A; + GroupOperations.ge_scalarmult_base(out A, publicKey.Array, publicKey.Offset); + FieldElement publicKeyFE; + EdwardsToMontgomeryX(out publicKeyFE, ref A.Y, ref A.Z); + FieldOperations.fe_tobytes(publicKey.Array, publicKey.Offset, ref publicKeyFE); + } + + // hashes like the Curve25519 paper says + internal static void KeyExchangeOutputHashCurve25519Paper(byte[] sharedKey, int offset) + { + //c = Curve25519output + const UInt32 c0 = 'C' | 'u' << 8 | 'r' << 16 | (UInt32)'v' << 24; + const UInt32 c1 = 'e' | '2' << 8 | '5' << 16 | (UInt32)'5' << 24; + const UInt32 c2 = '1' | '9' << 8 | 'o' << 16 | (UInt32)'u' << 24; + const UInt32 c3 = 't' | 'p' << 8 | 'u' << 16 | (UInt32)'t' << 24; + + Array16 salsaState; + salsaState.x0 = c0; + salsaState.x1 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 0); + salsaState.x2 = 0; + salsaState.x3 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 4); + salsaState.x4 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 8); + salsaState.x5 = c1; + salsaState.x6 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 12); + salsaState.x7 = 0; + salsaState.x8 = 0; + salsaState.x9 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 16); + salsaState.x10 = c2; + salsaState.x11 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 20); + salsaState.x12 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 24); + salsaState.x13 = 0; + salsaState.x14 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 28); + salsaState.x15 = c3; + SalsaCore.Salsa(out salsaState, ref salsaState, 20); + + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 0, salsaState.x0); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 4, salsaState.x1); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 8, salsaState.x2); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 12, salsaState.x3); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 16, salsaState.x4); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 20, salsaState.x5); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 24, salsaState.x6); + ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 28, salsaState.x7); + } + + private static readonly byte[] _zero16 = new byte[16]; + + // hashes like the NaCl paper says instead i.e. HSalsa(x,0) + internal static void KeyExchangeOutputHashNaCl(byte[] sharedKey, int offset) + { + Salsa20.HSalsa20(sharedKey, offset, sharedKey, offset, _zero16, 0); + } + + internal static byte[] KeyExchange(byte[] publicKey, byte[] privateKey) + { + var sharedKey = new byte[SharedKeySizeInBytes]; + KeyExchange(new ArraySegment(sharedKey), new ArraySegment(publicKey), new ArraySegment(privateKey)); + return sharedKey; + } + + internal static void KeyExchange(ArraySegment sharedKey, ArraySegment publicKey, ArraySegment privateKey) + { + if (sharedKey.Array == null) + throw new ArgumentNullException("sharedKey.Array"); + if (publicKey.Array == null) + throw new ArgumentNullException("publicKey.Array"); + if (privateKey.Array == null) + throw new ArgumentNullException("privateKey"); + if (sharedKey.Count != 32) + throw new ArgumentException("sharedKey.Count != 32"); + if (publicKey.Count != 32) + throw new ArgumentException("publicKey.Count != 32"); + if (privateKey.Count != 32) + throw new ArgumentException("privateKey.Count != 32"); + MontgomeryOperations.scalarmult(sharedKey.Array, sharedKey.Offset, privateKey.Array, privateKey.Offset, publicKey.Array, publicKey.Offset); + KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset); + } + + internal static void EdwardsToMontgomeryX(out FieldElement montgomeryX, ref FieldElement edwardsY, ref FieldElement edwardsZ) + { + FieldElement tempX, tempZ; + FieldOperations.fe_add(out tempX, ref edwardsZ, ref edwardsY); + FieldOperations.fe_sub(out tempZ, ref edwardsZ, ref edwardsY); + FieldOperations.fe_invert(out tempZ, ref tempZ); + FieldOperations.fe_mul(out montgomeryX, ref tempX, ref tempZ); + } + } +} diff --git a/src/Renci.SshNet/Security/Chaos.NaCl/Sha512.cs b/src/Renci.SshNet/Security/Chaos.NaCl/Sha512.cs new file mode 100644 index 000000000..f68d6ab8b --- /dev/null +++ b/src/Renci.SshNet/Security/Chaos.NaCl/Sha512.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using Renci.SshNet.Security.Chaos.NaCl.Internal; + +namespace Renci.SshNet.Security.Chaos.NaCl +{ + internal class Sha512 + { + private Array8 _state; + private readonly byte[] _buffer; + private ulong _totalBytes; + public const int BlockSize = 128; + private static readonly byte[] _padding = new byte[] { 0x80 }; + + public Sha512() + { + _buffer = new byte[BlockSize];//todo: remove allocation + Init(); + } + + public void Init() + { + Sha512Internal.Sha512Init(out _state); + _totalBytes = 0; + } + + public void Update(ArraySegment data) + { + if (data.Array == null) + throw new ArgumentNullException("data.Array"); + Update(data.Array, data.Offset, data.Count); + } + + public void Update(byte[] data, int offset, int count) + { + if (data == null) + throw new ArgumentNullException("data"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + if (data.Length - offset < count) + throw new ArgumentException("Requires offset + count <= data.Length"); + + Array16 block; + int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); + _totalBytes += (uint)count; + + if (_totalBytes >= ulong.MaxValue / 8) + throw new InvalidOperationException("Too much data"); + // Fill existing buffer + if (bytesInBuffer != 0) + { + var toCopy = Math.Min(BlockSize - bytesInBuffer, count); + Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, toCopy); + offset += toCopy; + count -= toCopy; + bytesInBuffer += toCopy; + if (bytesInBuffer == BlockSize) + { + ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); + Sha512Internal.Core(out _state, ref _state, ref block); + CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); + bytesInBuffer = 0; + } + } + // Hash complete blocks without copying + while (count >= BlockSize) + { + ByteIntegerConverter.Array16LoadBigEndian64(out block, data, offset); + Sha512Internal.Core(out _state, ref _state, ref block); + offset += BlockSize; + count -= BlockSize; + } + // Copy remainder into buffer + if (count > 0) + { + Buffer.BlockCopy(data, offset, _buffer, bytesInBuffer, count); + } + } + + public void Finish(ArraySegment output) + { + if (output.Array == null) + throw new ArgumentNullException("output.Array"); + if (output.Count != 64) + throw new ArgumentException("output.Count must be 64"); + + Update(_padding, 0, _padding.Length); + Array16 block; + ByteIntegerConverter.Array16LoadBigEndian64(out block, _buffer, 0); + CryptoBytes.InternalWipe(_buffer, 0, _buffer.Length); + int bytesInBuffer = (int)_totalBytes & (BlockSize - 1); + if (bytesInBuffer > BlockSize - 16) + { + Sha512Internal.Core(out _state, ref _state, ref block); + block = default(Array16); + } + block.x15 = (_totalBytes - 1) * 8; + Sha512Internal.Core(out _state, ref _state, ref block); + + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 0, _state.x0); + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 8, _state.x1); + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 16, _state.x2); + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 24, _state.x3); + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 32, _state.x4); + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 40, _state.x5); + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 48, _state.x6); + ByteIntegerConverter.StoreBigEndian64(output.Array, output.Offset + 56, _state.x7); + _state = default(Array8); + } + + public byte[] Finish() + { + var result = new byte[64]; + Finish(new ArraySegment(result)); + return result; + } + + internal static byte[] Hash(byte[] data) + { + return Hash(data, 0, data.Length); + } + + internal static byte[] Hash(byte[] data, int offset, int count) + { + var hasher = new Sha512(); + hasher.Update(data, offset, count); + return hasher.Finish(); + } + } +} From 465451985ae1f1b43a1009ee7427c6b3e0d7c785 Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Sat, 1 Dec 2018 08:04:00 +0100 Subject: [PATCH 3/8] KEX with Elliptic Curves ecdh-sha2-nistp{256,384,521} Add Kex Algos ecdh-sha2-nistp{256,384,521} We have to use a minimalistic BouncyCastle Import for ECDH, since Microsoft's System.Security.Cryptography is not usable in this case. ECDiffieHellmanCng.DeriveKeyMaterial() already does the hashing and it's not possible to get the unhased key material for further processing. https://blogs.msdn.microsoft.com/shawnfa/2007/01/22/elliptic-curve-diffie-hellman/ --- .../Renci.SshNet.Tests.NET35.csproj | 5 +- .../Common/ExtensionsTest_ToBigInteger2.cs | 40 ++++ .../Renci.SshNet.Tests.csproj | 3 +- src/Renci.SshNet/Common/Extensions.cs | 14 ++ src/Renci.SshNet/ConnectionInfo.cs | 7 +- .../Transport/KeyExchangeEcdhInitMessage.cs | 23 +- .../Transport/KeyExchangeEcdhReplyMessage.cs | 15 +- src/Renci.SshNet/Renci.SshNet.csproj | 5 + src/Renci.SshNet/Security/KeyExchangeEC.cs | 215 ++++++++++++++++++ src/Renci.SshNet/Security/KeyExchangeECDH.cs | 107 +++++++++ .../Security/KeyExchangeECDH256.cs | 38 ++++ .../Security/KeyExchangeECDH384.cs | 54 +++++ .../Security/KeyExchangeECDH521.cs | 54 +++++ src/Renci.SshNet/Session.cs | 12 + src/Renci.SshNet/SshMessageFactory.cs | 5 +- 15 files changed, 575 insertions(+), 22 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs create mode 100644 src/Renci.SshNet/Security/KeyExchangeEC.cs create mode 100644 src/Renci.SshNet/Security/KeyExchangeECDH.cs create mode 100644 src/Renci.SshNet/Security/KeyExchangeECDH256.cs create mode 100644 src/Renci.SshNet/Security/KeyExchangeECDH384.cs create mode 100644 src/Renci.SshNet/Security/KeyExchangeECDH521.cs diff --git a/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj b/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj index a42cf8ec1..058ec2ae5 100644 --- a/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj +++ b/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj @@ -351,6 +351,9 @@ Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs + + Classes\Common\ExtensionsTest_ToBigInteger2.cs + Classes\Common\ExtensionsTest_TrimLeadingZeros.cs @@ -1750,4 +1753,4 @@ --> - \ No newline at end of file + diff --git a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs new file mode 100644 index 000000000..1299f17ad --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_ToBigInteger2.cs @@ -0,0 +1,40 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Tests.Classes.Common +{ + [TestClass] + [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")] + public class ExtensionsTest_ToBigInteger2 + { + [TestMethod] + public void ShouldNotAppendZero() + { + byte[] value = { 0x0a, 0x0d }; + + var actual = value.ToBigInteger2().ToByteArray().Reverse(); + + Assert.IsNotNull(actual); + Assert.AreEqual(2, actual.Length); + Assert.AreEqual(0x0a, actual[0]); + Assert.AreEqual(0x0d, actual[1]); + } + + [TestMethod] + public void ShouldAppendZero() + { + byte[] value = { 0xff, 0x0a, 0x0d }; + + var actual = value.ToBigInteger2().ToByteArray().Reverse(); + + Assert.IsNotNull(actual); + Assert.AreEqual(4, actual.Length); + Assert.AreEqual(0x00, actual[0]); + Assert.AreEqual(0xff, actual[1]); + Assert.AreEqual(0x0a, actual[2]); + Assert.AreEqual(0x0d, actual[3]); + } + } +} diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index 0575dbd82..bb99b0c5a 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -169,6 +169,7 @@ + @@ -717,4 +718,4 @@ --> - + \ No newline at end of file diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs index 9d163bc96..784139ab3 100644 --- a/src/Renci.SshNet/Common/Extensions.cs +++ b/src/Renci.SshNet/Common/Extensions.cs @@ -72,6 +72,20 @@ internal static BigInteger ToBigInteger(this byte[] data) return new BigInteger(reversed.Reverse()); } + /// + /// Initializes a new instance of the structure using the SSH BigNum2 Format + /// + public static BigInteger ToBigInteger2(this byte[] data) + { + if ((data[0] & (1 << 7)) != 0) + { + var buf = new byte[data.Length + 1]; + Buffer.BlockCopy(data, 0, buf, 1, data.Length); + data = buf; + } + return data.ToBigInteger(); + } + /// /// Reverses the sequence of the elements in the entire one-dimensional . /// diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index f9b872d3c..780b7918e 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -322,14 +322,13 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy KeyExchangeAlgorithms = new Dictionary { + {"ecdh-sha2-nistp256", typeof(KeyExchangeECDH256)}, + {"ecdh-sha2-nistp384", typeof(KeyExchangeECDH384)}, + {"ecdh-sha2-nistp521", typeof(KeyExchangeECDH521)}, {"diffie-hellman-group-exchange-sha256", typeof (KeyExchangeDiffieHellmanGroupExchangeSha256)}, {"diffie-hellman-group-exchange-sha1", typeof (KeyExchangeDiffieHellmanGroupExchangeSha1)}, {"diffie-hellman-group14-sha1", typeof (KeyExchangeDiffieHellmanGroup14Sha1)}, {"diffie-hellman-group1-sha1", typeof (KeyExchangeDiffieHellmanGroup1Sha1)}, - //{"ecdh-sha2-nistp256", typeof(KeyExchangeEllipticCurveDiffieHellman)}, - //{"ecdh-sha2-nistp256", typeof(...)}, - //{"ecdh-sha2-nistp384", typeof(...)}, - //{"ecdh-sha2-nistp521", typeof(...)}, //"gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD //"gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD }; diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs index 2620d982a..ddcf03f19 100644 --- a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs +++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs @@ -1,6 +1,4 @@ -#if false - -using System; +using System; using Renci.SshNet.Common; namespace Renci.SshNet.Messages.Transport @@ -8,7 +6,7 @@ namespace Renci.SshNet.Messages.Transport /// /// Represents SSH_MSG_KEXECDH_INIT message. /// - [Message("SSH_MSG_KEXECDH_INIT", 30)] + [Message("SSH_MSG_KEX_ECDH_INIT", 30)] internal class KeyExchangeEcdhInitMessage : Message, IKeyExchangedAllowed { /// @@ -33,6 +31,14 @@ protected override int BufferCapacity } } + /// + /// Initializes a new instance of the class. + /// + public KeyExchangeEcdhInitMessage(byte[] q) + { + QC = q; + } + /// /// Initializes a new instance of the class. /// @@ -63,7 +69,10 @@ protected override void SaveData() { WriteBinaryString(QC); } - } -} -#endif // false \ No newline at end of file + internal override void Process(Session session) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs index 42c363150..a194caf1e 100644 --- a/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs +++ b/src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhReplyMessage.cs @@ -1,11 +1,9 @@ -#if false - -namespace Renci.SshNet.Messages.Transport +namespace Renci.SshNet.Messages.Transport { /// /// Represents SSH_MSG_KEXECDH_REPLY message. /// - [Message("SSH_MSG_KEXECDH_REPLY", 31)] + [Message("SSH_MSG_KEX_ECDH_REPLY", 31)] public class KeyExchangeEcdhReplyMessage : Message { /// @@ -65,7 +63,10 @@ protected override void SaveData() WriteBinaryString(QS); WriteBinaryString(Signature); } - } -} -#endif // false \ No newline at end of file + internal override void Process(Session session) + { + session.OnKeyExchangeEcdhReplyMessageReceived(this); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index b0b4b8998..e1bb56ba7 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -309,6 +309,11 @@ + + + + + diff --git a/src/Renci.SshNet/Security/KeyExchangeEC.cs b/src/Renci.SshNet/Security/KeyExchangeEC.cs new file mode 100644 index 000000000..448569097 --- /dev/null +++ b/src/Renci.SshNet/Security/KeyExchangeEC.cs @@ -0,0 +1,215 @@ +using System; +using System.Text; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Common; +using Renci.SshNet.Abstractions; + +namespace Renci.SshNet.Security +{ + internal abstract class KeyExchangeEC : KeyExchange + { + /// + /// Specifies client payload + /// + protected byte[] _clientPayload; + + /// + /// Specifies server payload + /// + protected byte[] _serverPayload; + + /// + /// Specifies client exchange. + /// + protected byte[] _clientExchangeValue; + + /// + /// Specifies server exchange. + /// + protected byte[] _serverExchangeValue; + + /// + /// Specifies host key data. + /// + protected byte[] _hostKey; + + /// + /// Specifies signature data. + /// + protected byte[] _signature; + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// + /// The size, in bits, of the computed hash code. + /// + protected abstract int HashSize { get; } + + /// + /// Hashes the specified data bytes. + /// + /// The hash data. + /// + /// Hashed bytes + /// + protected override byte[] Hash(byte[] hashData) + { + using (var sha256 = CryptoAbstraction.CreateSHA256()) + { + return sha256.ComputeHash(hashData, 0, hashData.Length); + } + } + + /// + /// Calculates key exchange hash value. + /// + /// + /// Key exchange hash. + /// + protected override byte[] CalculateHash() + { + var hashData = new _ExchangeHashData + { + ClientVersion = Session.ClientVersion, + ServerVersion = Session.ServerVersion, + ClientPayload = _clientPayload, + ServerPayload = _serverPayload, + HostKey = _hostKey, + ClientExchangeValue = _clientExchangeValue, + ServerExchangeValue = _serverExchangeValue, + SharedKey = SharedKey, + }.GetBytes(); + + return Hash(hashData); + } + + /// + /// Validates the exchange hash. + /// + /// + /// true if exchange hash is valid; otherwise false. + /// + protected override bool ValidateExchangeHash() + { + var exchangeHash = CalculateHash(); + + var length = Pack.BigEndianToUInt32(_hostKey); + var algorithmName = Encoding.UTF8.GetString(_hostKey, 4, (int)length); + var key = Session.ConnectionInfo.HostKeyAlgorithms[algorithmName](_hostKey); + + Session.ConnectionInfo.CurrentHostKeyAlgorithm = algorithmName; + + if (CanTrustHostKey(key)) + { + return key.VerifySignature(exchangeHash, _signature); + } + return false; + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + _serverPayload = message.GetBytes(); + _clientPayload = Session.ClientInitMessage.GetBytes(); + } + + /// + /// Handles the server DH reply message. + /// + /// The host key. + /// The server exchange value. + /// The signature. + protected virtual void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature) + { + } + + internal class _ExchangeHashData : SshData + { + private byte[] _serverVersion; + private byte[] _clientVersion; + private byte[] _sharedKey; + + public string ServerVersion + { + private get { return Utf8.GetString(_serverVersion, 0, _serverVersion.Length); } + set { _serverVersion = Utf8.GetBytes(value); } + } + + public string ClientVersion + { + private get { return Utf8.GetString(_clientVersion, 0, _clientVersion.Length); } + set { _clientVersion = Utf8.GetBytes(value); } + } + + public byte[] ClientPayload { get; set; } + + public byte[] ServerPayload { get; set; } + + public byte[] HostKey { get; set; } + + public byte[] ClientExchangeValue { get; set; } + + public byte[] ServerExchangeValue { get; set; } + + public BigInteger SharedKey + { + private get { return _sharedKey.ToBigInteger(); } + set { _sharedKey = value.ToByteArray().Reverse(); } + } + /// + /// Gets the size of the message in bytes. + /// + /// + /// The size of the messages in bytes. + /// + protected override int BufferCapacity + { + get + { + var capacity = base.BufferCapacity; + capacity += 4; // ClientVersion length + capacity += _clientVersion.Length; // ClientVersion + capacity += 4; // ServerVersion length + capacity += _serverVersion.Length; // ServerVersion + capacity += 4; // ClientPayload length + capacity += ClientPayload.Length; // ClientPayload + capacity += 4; // ServerPayload length + capacity += ServerPayload.Length; // ServerPayload + capacity += 4; // HostKey length + capacity += HostKey.Length; // HostKey + capacity += 4; // ClientExchangeValue length + capacity += ClientExchangeValue.Length; // ClientExchangeValue + capacity += 4; // ServerExchangeValue length + capacity += ServerExchangeValue.Length; // ServerExchangeValue + capacity += 4; // SharedKey length + capacity += _sharedKey.Length; // SharedKey + return capacity; + } + } + + protected override void LoadData() + { + throw new NotImplementedException(); + } + + protected override void SaveData() + { + WriteBinaryString(_clientVersion); + WriteBinaryString(_serverVersion); + WriteBinaryString(ClientPayload); + WriteBinaryString(ServerPayload); + WriteBinaryString(HostKey); + WriteBinaryString(ClientExchangeValue); + WriteBinaryString(ServerExchangeValue); + WriteBinaryString(_sharedKey); + } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH.cs b/src/Renci.SshNet/Security/KeyExchangeECDH.cs new file mode 100644 index 000000000..9c5d4bfdd --- /dev/null +++ b/src/Renci.SshNet/Security/KeyExchangeECDH.cs @@ -0,0 +1,107 @@ +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; + +using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Generators; +using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Parameters; +using Renci.SshNet.Security.Org.BouncyCastle.Security; +using Renci.SshNet.Security.Org.BouncyCastle.Math.EC; +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; +using Renci.SshNet.Security.Org.BouncyCastle.Crypto.Agreement; + +namespace Renci.SshNet.Security +{ + internal abstract class KeyExchangeECDH : KeyExchangeEC + { + /// + /// Gets the parameter of the curve. + /// + /// + /// The parameter of the curve. + /// + protected abstract X9ECParameters CurveParameter { get; } + + protected ECDHCBasicAgreement KeyAgreement; + protected ECDomainParameters DomainParameters; + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); + + Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; + + DomainParameters = new ECDomainParameters(CurveParameter.Curve, + CurveParameter.G, + CurveParameter.N, + CurveParameter.H, + CurveParameter.GetSeed()); + + var g = new ECKeyPairGenerator(); + g.Init(new ECKeyGenerationParameters(DomainParameters, new SecureRandom())); + + var aKeyPair = g.GenerateKeyPair(); + KeyAgreement = new ECDHCBasicAgreement(); + KeyAgreement.Init(aKeyPair.Private); + _clientExchangeValue = ((ECPublicKeyParameters)aKeyPair.Public).Q.GetEncoded(); + + SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); + } + + /// + /// Finishes key exchange algorithm. + /// + public override void Finish() + { + base.Finish(); + + Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived; + } + + private void Session_KeyExchangeEcdhReplyMessageReceived(object sender, MessageEventArgs e) + { + var message = e.Message; + + // Unregister message once received + Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); + + HandleServerEcdhReply(message.KS, message.QS, message.Signature); + + // When SSH_MSG_KEXDH_REPLY received key exchange is completed + Finish(); + } + + /// + /// Handles the server DH reply message. + /// + /// The host key. + /// The server exchange value. + /// The signature. + protected override void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature) + { + _serverExchangeValue = serverExchangeValue; + _hostKey = hostKey; + _serverExchangeValue = serverExchangeValue; + _signature = signature; + + var cordSize = (serverExchangeValue.Length - 1) / 2; + var x = new byte[cordSize]; + Buffer.BlockCopy(serverExchangeValue, 1, x, 0, x.Length); // first byte is format. should be checked and passed to bouncy castle? + var y = new byte[cordSize]; + Buffer.BlockCopy(serverExchangeValue, cordSize + 1, y, 0, y.Length); + + var c = (FpCurve)DomainParameters.Curve; + var q = c.CreatePoint(new Org.BouncyCastle.Math.BigInteger(1, x), new Org.BouncyCastle.Math.BigInteger(1, y)); + var publicKey = new ECPublicKeyParameters("ECDH", q, DomainParameters); + + var k1 = KeyAgreement.CalculateAgreement(publicKey); + SharedKey = k1.ToByteArray().ToBigInteger2(); + } + } +} diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH256.cs b/src/Renci.SshNet/Security/KeyExchangeECDH256.cs new file mode 100644 index 000000000..84a8d12b8 --- /dev/null +++ b/src/Renci.SshNet/Security/KeyExchangeECDH256.cs @@ -0,0 +1,38 @@ +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec; +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; + +namespace Renci.SshNet.Security +{ + internal class KeyExchangeECDH256 : KeyExchangeECDH + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "ecdh-sha2-nistp256"; } + } + + /// + /// Gets Curve Parameter. + /// + protected override X9ECParameters CurveParameter + { + get + { + return SecNamedCurves.GetByName("P-256"); + } + } + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// + /// The size, in bits, of the computed hash code. + /// + protected override int HashSize + { + get { return 256; } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH384.cs b/src/Renci.SshNet/Security/KeyExchangeECDH384.cs new file mode 100644 index 000000000..bbd7ced51 --- /dev/null +++ b/src/Renci.SshNet/Security/KeyExchangeECDH384.cs @@ -0,0 +1,54 @@ +using Renci.SshNet.Abstractions; +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec; +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; + +namespace Renci.SshNet.Security +{ + internal class KeyExchangeECDH384 : KeyExchangeECDH + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "ecdh-sha2-nistp384"; } + } + + /// + /// Gets Curve Parameter. + /// + protected override X9ECParameters CurveParameter + { + get + { + return SecNamedCurves.GetByName("P-384"); + } + } + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// + /// The size, in bits, of the computed hash code. + /// + protected override int HashSize + { + get { return 384; } + } + + /// + /// Hashes the specified data bytes. + /// + /// The hash data. + /// + /// Hashed bytes + /// + protected override byte[] Hash(byte[] hashData) + { + using (var sha384 = CryptoAbstraction.CreateSHA384()) + { + return sha384.ComputeHash(hashData, 0, hashData.Length); + } + } + } +} diff --git a/src/Renci.SshNet/Security/KeyExchangeECDH521.cs b/src/Renci.SshNet/Security/KeyExchangeECDH521.cs new file mode 100644 index 000000000..920089c02 --- /dev/null +++ b/src/Renci.SshNet/Security/KeyExchangeECDH521.cs @@ -0,0 +1,54 @@ +using Renci.SshNet.Abstractions; +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.Sec; +using Renci.SshNet.Security.Org.BouncyCastle.Asn1.X9; + +namespace Renci.SshNet.Security +{ + internal class KeyExchangeECDH521 : KeyExchangeECDH + { + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "ecdh-sha2-nistp521"; } + } + + /// + /// Gets Curve Parameter. + /// + protected override X9ECParameters CurveParameter + { + get + { + return SecNamedCurves.GetByName("P-521"); + } + } + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// + /// The size, in bits, of the computed hash code. + /// + protected override int HashSize + { + get { return 512; } + } + + /// + /// Hashes the specified data bytes. + /// + /// The hash data. + /// + /// Hashed bytes + /// + protected override byte[] Hash(byte[] hashData) + { + using (var sha512 = CryptoAbstraction.CreateSHA512()) + { + return sha512.ComputeHash(hashData, 0, hashData.Length); + } + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 811b06dab..fd7710952 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -439,6 +439,11 @@ public Message ClientInitMessage /// internal event EventHandler> KeyExchangeDhReplyMessageReceived; + /// + /// Occurs when a message is received from the SSH server. + /// + internal event EventHandler> KeyExchangeEcdhReplyMessageReceived; + /// /// Occurs when message received /// @@ -1322,6 +1327,13 @@ internal void OnKeyExchangeDhReplyMessageReceived(KeyExchangeDhReplyMessage mess handlers(this, new MessageEventArgs(message)); } + internal void OnKeyExchangeEcdhReplyMessageReceived(KeyExchangeEcdhReplyMessage message) + { + var handlers = KeyExchangeEcdhReplyMessageReceived; + if (handlers != null) + handlers(this, new MessageEventArgs(message)); + } + /// /// Called when message received. /// diff --git a/src/Renci.SshNet/SshMessageFactory.cs b/src/Renci.SshNet/SshMessageFactory.cs index 9041ea345..153024dbf 100644 --- a/src/Renci.SshNet/SshMessageFactory.cs +++ b/src/Renci.SshNet/SshMessageFactory.cs @@ -25,7 +25,7 @@ internal class SshMessageFactory /// /// Defines the total number of supported messages. /// - internal const int TotalMessageCount = 31; + internal const int TotalMessageCount = 32; static SshMessageFactory() { @@ -61,7 +61,8 @@ static SshMessageFactory() new MessageMetadata (27, "SSH_MSG_SERVICE_ACCEPT", 6), new MessageMetadata (28, "SSH_MSG_KEX_DH_GEX_GROUP", 31), new MessageMetadata (29, "SSH_MSG_KEXDH_REPLY", 31), - new MessageMetadata (30, "SSH_MSG_KEX_DH_GEX_REPLY", 33) + new MessageMetadata (30, "SSH_MSG_KEX_DH_GEX_REPLY", 33), + new MessageMetadata (31, "SSH_MSG_KEX_ECDH_REPLY", 31) }; MessagesByName = new Dictionary(AllMessages.Length); From b58a11c0da55da1f5bad46faad2e9b71b7cb35b3 Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Sat, 1 Dec 2018 08:04:00 +0100 Subject: [PATCH 4/8] KEX with Elliptic Curve curve25519 Add Kex Algos curve25519-sha256(@libssh.org) --- src/Renci.SshNet/ConnectionInfo.cs | 2 + src/Renci.SshNet/Renci.SshNet.csproj | 1 + .../Security/KeyExchangeECCurve25519.cs | 99 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index 780b7918e..a8317909a 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -322,6 +322,8 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy KeyExchangeAlgorithms = new Dictionary { + {"curve25519-sha256", typeof(KeyExchangeECCurve25519)}, + {"curve25519-sha256@libssh.org", typeof(KeyExchangeECCurve25519)}, {"ecdh-sha2-nistp256", typeof(KeyExchangeECDH256)}, {"ecdh-sha2-nistp384", typeof(KeyExchangeECDH384)}, {"ecdh-sha2-nistp521", typeof(KeyExchangeECDH521)}, diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index e1bb56ba7..625080160 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -310,6 +310,7 @@ + diff --git a/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs b/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs new file mode 100644 index 000000000..39658de07 --- /dev/null +++ b/src/Renci.SshNet/Security/KeyExchangeECCurve25519.cs @@ -0,0 +1,99 @@ +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Messages.Transport; +using Renci.SshNet.Security.Chaos.NaCl; +using Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10; + +namespace Renci.SshNet.Security +{ + internal class KeyExchangeECCurve25519 : KeyExchangeEC + { + private byte[] _privateKey; + + /// + /// Gets algorithm name. + /// + public override string Name + { + get { return "curve25519-sha256"; } + } + + /// + /// Gets the size, in bits, of the computed hash code. + /// + /// + /// The size, in bits, of the computed hash code. + /// + protected override int HashSize + { + get { return 256; } + } + + /// + /// Starts key exchange algorithm + /// + /// The session. + /// Key exchange init message. + public override void Start(Session session, KeyExchangeInitMessage message) + { + base.Start(session, message); + + Session.RegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); + + Session.KeyExchangeEcdhReplyMessageReceived += Session_KeyExchangeEcdhReplyMessageReceived; + + var basepoint = new byte[MontgomeryCurve25519.PublicKeySizeInBytes]; + basepoint[0] = 9; + + var rnd = new Random(); + _privateKey = new byte[MontgomeryCurve25519.PrivateKeySizeInBytes]; + rnd.NextBytes(_privateKey); + + _clientExchangeValue = new byte[MontgomeryCurve25519.PublicKeySizeInBytes]; + MontgomeryOperations.scalarmult(_clientExchangeValue, 0, _privateKey, 0, basepoint, 0); + + SendMessage(new KeyExchangeEcdhInitMessage(_clientExchangeValue)); + } + + /// + /// Finishes key exchange algorithm. + /// + public override void Finish() + { + base.Finish(); + + Session.KeyExchangeEcdhReplyMessageReceived -= Session_KeyExchangeEcdhReplyMessageReceived; + } + + private void Session_KeyExchangeEcdhReplyMessageReceived(object sender, MessageEventArgs e) + { + var message = e.Message; + + // Unregister message once received + Session.UnRegisterMessage("SSH_MSG_KEX_ECDH_REPLY"); + + HandleServerEcdhReply(message.KS, message.QS, message.Signature); + + // When SSH_MSG_KEXDH_REPLY received key exchange is completed + Finish(); + } + + /// + /// Handles the server DH reply message. + /// + /// The host key. + /// The server exchange value. + /// The signature. + protected override void HandleServerEcdhReply(byte[] hostKey, byte[] serverExchangeValue, byte[] signature) + { + _serverExchangeValue = serverExchangeValue; + _hostKey = hostKey; + _serverExchangeValue = serverExchangeValue; + _signature = signature; + + var sharedKey = new byte[MontgomeryCurve25519.PublicKeySizeInBytes]; + MontgomeryOperations.scalarmult(sharedKey, 0, _privateKey, 0, serverExchangeValue, 0); + SharedKey = sharedKey.ToBigInteger2(); + } + } +} From 90fb62b7cccc5f8688a86452be8930e63504cdca Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Mon, 26 Nov 2018 20:15:25 +0100 Subject: [PATCH 5/8] Add Support for ECDSA Host- and Private-Keys Also known as: - ecdsa-sha2-nistp256 - ecdsa-sha2-nistp384 - ecdsa-sha2-nistp521 Basically it translate between SSH-Data and Microsoft Crypto API. --- .../Renci.SshNet.NET35.csproj | 10 +- .../Renci.SshNet.NETCore.csproj | 3 +- .../Renci.SshNet.Tests.NET35.csproj | 23 + .../Classes/Common/ExtensionsTest_Pad.cs | 32 ++ .../Classes/PrivateKeyFileTest.cs | 66 +++ .../Data/Key.ECDSA.Encrypted.txt | 8 + src/Renci.SshNet.Tests/Data/Key.ECDSA.txt | 5 + .../Data/Key.ECDSA384.Encrypted.txt | 9 + src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt | 6 + .../Data/Key.ECDSA521.Encrypted.txt | 10 + src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt | 7 + .../Renci.SshNet.Tests.csproj | 11 +- src/Renci.SshNet/Common/DerData.cs | 120 ++++- src/Renci.SshNet/Common/Extensions.cs | 14 + src/Renci.SshNet/ConnectionInfo.cs | 6 +- src/Renci.SshNet/PrivateKeyFile.cs | 19 +- src/Renci.SshNet/Renci.SshNet.csproj | 6 +- .../Cryptography/EcdsaDigitalSignature.cs | 189 ++++++++ .../Security/Cryptography/EcdsaKey.cs | 459 ++++++++++++++++++ 19 files changed, 981 insertions(+), 22 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs create mode 100644 src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.ECDSA.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt create mode 100644 src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs create mode 100644 src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs diff --git a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj index a41e7bcb3..0df2f4f50 100644 --- a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj +++ b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj @@ -18,7 +18,7 @@ full false bin\Debug\ - TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII + TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA prompt 4 true @@ -29,7 +29,7 @@ none true bin\Release\ - TRACE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII + TRACE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA prompt 4 bin\Release\Renci.SshNet.xml @@ -911,6 +911,12 @@ Security\Cryptography\Key.cs + + Security\Cryptography\EcdsaDigitalSignature.cs + + + Security\Cryptography\EcdsaKey.cs + Security\Cryptography\RsaDigitalSignature.cs diff --git a/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj b/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj index bf001750e..b288064e6 100644 --- a/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj +++ b/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj @@ -27,11 +27,12 @@ + FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_DNS_TAP;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 - 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 + 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;FEATURE_ECDSA diff --git a/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj b/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj index 058ec2ae5..ec5feb95e 100644 --- a/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj +++ b/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj @@ -354,6 +354,9 @@ Classes\Common\ExtensionsTest_ToBigInteger2.cs + + Classes\Common\ExtensionsTest_Pad.cs + Classes\Common\ExtensionsTest_TrimLeadingZeros.cs @@ -1740,6 +1743,26 @@ Data\Key.SSH2.RSA.txt + + + Data\Key.ECDSA.txt + + + Data\Key.ECDSA384.txt + + + Data\Key.ECDSA521.txt + + + Data\Key.ECDSA.Encrypted.txt + + + Data\Key.ECDSA384.Encrypted.txt + + + Data\Key.ECDSA521.Encrypted.txt + + diff --git a/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs new file mode 100644 index 000000000..f39c45ded --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Pad.cs @@ -0,0 +1,32 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Renci.SshNet.Common; + +namespace Renci.SshNet.Tests.Classes.Common +{ + [TestClass] + [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")] + public class ExtensionsTest_Pad + { + [TestMethod] + public void ShouldReturnNotPadded() + { + byte[] value = {0x0a, 0x0d}; + byte[] padded = value.Pad(2); + Assert.AreEqual(value, padded); + Assert.AreEqual(value.Length, padded.Length); + } + + [TestMethod] + public void ShouldReturnPadded() + { + byte[] value = { 0x0a, 0x0d }; + byte[] padded = value.Pad(3); + Assert.AreEqual(value.Length + 1, padded.Length); + Assert.AreEqual(0x00, padded[0]); + Assert.AreEqual(0x0a, padded[1]); + Assert.AreEqual(0x0d, padded[2]); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs index 21bd97853..3c944c8d4 100644 --- a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs +++ b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs @@ -319,6 +319,72 @@ public void Test_PrivateKey_RSA_DES_EDE3_CFB() } } + [TestMethod] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_ECDSA() + { + using (var stream = GetData("Key.ECDSA.txt")) + { + new PrivateKeyFile(stream); + } + } + + [TestMethod] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_ECDSA384() + { + using (var stream = GetData("Key.ECDSA384.txt")) + { + new PrivateKeyFile(stream); + } + } + + [TestMethod] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_ECDSA521() + { + using (var stream = GetData("Key.ECDSA521.txt")) + { + new PrivateKeyFile(stream); + } + } + + [TestMethod] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_ECDSA_Encrypted() + { + using (var stream = GetData("Key.ECDSA.Encrypted.txt")) + { + new PrivateKeyFile(stream, "12345"); + } + } + + [TestMethod] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_ECDSA384_Encrypted() + { + using (var stream = GetData("Key.ECDSA384.Encrypted.txt")) + { + new PrivateKeyFile(stream, "12345"); + } + } + + [TestMethod] + [Owner("darinkes")] + [TestCategory("PrivateKey")] + public void Test_PrivateKey_ECDSA521_Encrypted() + { + using (var stream = GetData("Key.ECDSA521.Encrypted.txt")) + { + new PrivateKeyFile(stream, "12345"); + } + } + /// ///A test for Dispose /// diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt new file mode 100644 index 000000000..f0af5ba7d --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt @@ -0,0 +1,8 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,54D46F498C989115AAE14FEA21E3AF11 + +IQdFnndcbzz10d7YQIgEE1TzuzJrm7uYJr4Hvdfz/FshVxMRqxqaqtEgo2vAHHik +BOcPkm+84ERlTNPslcJqLSkKzCdxb7Rz5hfwHuN3Y6Lf01qGakDlzAUEjEyDor+4 +zQtAne+f+gRUJnBvLLoVhH4xdeQFC55GECNUFQpEmos= +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA.txt b/src/Renci.SshNet.Tests/Data/Key.ECDSA.txt new file mode 100644 index 000000000..13ac9fb49 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.ECDSA.txt @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIEdqaFKgJBIibVjyUh1v7Y35LwIQJrocdTaYFLwl7iB0oAoGCCqGSM49 +AwEHoUQDQgAEQD5MO/n9yqSDTszwzVpApLx5SQFecE5ZfDkgxqVdHQecm1BAPozZ +4eKGNhKn72hT79mLlp9HXX+oNEcuVT83Hw== +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt new file mode 100644 index 000000000..00072ce24 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt @@ -0,0 +1,9 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,1D64653C5E18C2AACB0B17E3FE43C219 + +lCtRmcvKSeIACwqTtsf/ei1brtCZ386rsk/j7bSXdkZBpvzcmzbeo6w6CYm206Km +hV9TMl2dIO/I1/ov5/2VIR3ZkaElyDOJD/+Be0e3aus4EZj1H1YM/Dv+4QJId+is +Cw4ycWjfudYPPejGdiyjzt5qjaIJwrrEvGtMg7sWVAqDpjcAjS9KuaCu5nOgdItL +s7oHuz+DTGdJQNfUHAlUnz1JaMRWzpP0MwtxdcaRY+w= +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt b/src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt new file mode 100644 index 000000000..f2d658ea4 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDCQawHdHLR7NvKa2vPV0sVkbzOE8c0enp95iEysGcGV66RXE1EH//nh +gu5UzeTR4KigBwYFK4EEACKhZANiAAQUk4rVvoOPI1hQzWpNx09Uo6qG+srGcbvB +q15eFK0GnK/T0UBKxdbZ2+//KAYI6SeDHM9t3ORF1aX5EpjTEBI4d7ZY/lV9jX6M +nJ4XuGteJselM2iMmy+p9ZYw83BYB1Y= +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt new file mode 100644 index 000000000..381b30be8 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt @@ -0,0 +1,10 @@ +-----BEGIN EC PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,F995028237EBD79C928530CC6C3E957F + +wT+iajbte4MnpCipVy/7W9t2I8OgwbMjNBw9PB5xmXR1NQX+yWa81DXMTgjHi8++ +6tp+Vlftkr7mY1yvZCVo1Sy4VgcvZeMhtpVKtvYdMCmHJC6gaDOTYX3yee8DJ4FL +fG+IQz0wFyZZ26NFrHiwbufW9z6pXhGNCQZK0KLbFxI9iKwVA0llc7uzTEcmBBpn +0/Snp0CVvX+i6AP9Xj0bBdrFCsvcoT+ZHzS8YWJUfu3m6cpAJksCAy0PXR3ifvus +edTfDpkMxd4/b+DtPB6SMekIAjnQyzbyaTwJCujm8iU= +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt b/src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt new file mode 100644 index 000000000..31abe917a --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBn2DAme7AU8sCA+/sd6s3c2FNW26IiPvulGd3FC8k5q+fjBZ5LUWR +iJMGrsf2rJLO8hXMGJYoF9tjZEGaabQ8KVagBwYFK4EEACOhgYkDgYYABABrpVjs +ANqcvqMUo1wo0I1uVCXQ6xrauy4iU86FiOwFmkYRrle4w3oYdRJwniC3TwGMuBuM +PMIoCTXr0UtUzn1vkQESNR/J/jAxVseLlVe+KDfZHKvsvk2+O4XaSa1qMfLwN3sp +wlj08+ylKjlO6V3g0hbz4ZaSVwuiRS7Xsv8W2MV6rg== +-----END EC PRIVATE KEY----- \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index bb99b0c5a..f32beeed3 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -170,6 +170,7 @@ + @@ -710,6 +711,14 @@ Renci.SshNet + + + + + + + + - \ No newline at end of file + diff --git a/src/Renci.SshNet/Common/DerData.cs b/src/Renci.SshNet/Common/DerData.cs index c84c0b176..35178798a 100644 --- a/src/Renci.SshNet/Common/DerData.cs +++ b/src/Renci.SshNet/Common/DerData.cs @@ -12,7 +12,7 @@ public class DerData private const byte Boolean = 0x01; private const byte Integer = 0x02; - //private const byte BITSTRING = 0x03; + private const byte BITSTRING = 0x03; private const byte Octetstring = 0x04; private const byte Null = 0x05; private const byte Objectidentifier = 0x06; @@ -70,12 +70,20 @@ public DerData() /// Initializes a new instance of the class. /// /// DER encoded data. - public DerData(byte[] data) + /// its a construct + public DerData(byte[] data, bool construct = false) { _data = new List(data); - ReadByte(); // skip dataType - var length = ReadLength(); - _lastIndex = _readerIndex + length; + if (construct) + { + _lastIndex = _readerIndex + data.Length; + } + else + { + ReadByte(); // skip dataType + var length = ReadLength(); + _lastIndex = _readerIndex + length; + } } /// @@ -101,7 +109,7 @@ public BigInteger ReadBigInteger() { var type = ReadByte(); if (type != Integer) - throw new InvalidOperationException("Invalid data type, INTEGER(02) is expected."); + throw new InvalidOperationException(string.Format("Invalid data type, INTEGER(02) is expected, but was {0}", type.ToString("X2"))); var length = ReadLength(); @@ -118,7 +126,7 @@ public int ReadInteger() { var type = ReadByte(); if (type != Integer) - throw new InvalidOperationException("Invalid data type, INTEGER(02) is expected."); + throw new InvalidOperationException(string.Format("Invalid data type, INTEGER(02) is expected, but was {0}", type.ToString("X2"))); var length = ReadLength(); @@ -140,6 +148,51 @@ public int ReadInteger() return result; } + /// + /// Reads next octetstring data type from internal buffer. + /// + /// data read. + public byte[] ReadOctetString() + { + var type = ReadByte(); + if (type != Octetstring) + throw new InvalidOperationException(string.Format("Invalid data type, OCTETSTRING(04) is expected, but was {0}", type.ToString("X2"))); + + var length = ReadLength(); + var data = ReadBytes(length); + return data; + } + + /// + /// Reads next bitstring data type from internal buffer. + /// + /// data read. + public byte[] ReadBitString() + { + var type = ReadByte(); + if (type != BITSTRING) + throw new InvalidOperationException(string.Format("Invalid data type, BITSTRING(03) is expected, but was {0}", type.ToString("X2"))); + + var length = ReadLength(); + var data = ReadBytes(length); + return data; + } + + /// + /// Reads next object data type from internal buffer. + /// + /// data read. + public byte[] ReadObject() + { + var type = ReadByte(); + if (type != Objectidentifier) + throw new InvalidOperationException(string.Format("Invalid data type, OBJECT(06) is expected, but was {0}", type.ToString("X2"))); + + var length = ReadLength(); + var data = ReadBytes(length); + return data; + } + /// /// Writes BOOLEAN data into internal buffer. /// @@ -189,6 +242,18 @@ public void Write(byte[] data) WriteBytes(data); } + /// + /// Writes BITSTRING data into internal buffer. + /// + /// The data. + public void WriteBitstring(byte[] data) + { + _data.Add(BITSTRING); + var length = GetLength(data.Length); + WriteBytes(length); + WriteBytes(data); + } + /// /// Writes OBJECTIDENTIFIER data into internal buffer. /// @@ -229,6 +294,18 @@ public void Write(ObjectIdentifier identifier) WriteBytes(bytes); } + /// + /// Writes OBJECTIDENTIFIER data into internal buffer. + /// + /// The bytes. + public void WriteObjectIdentifier(byte[] bytes) + { + _data.Add(Objectidentifier); + var length = GetLength(bytes.Length); + WriteBytes(length); + WriteBytes(bytes); + } + /// /// Writes NULL data into internal buffer. /// @@ -268,10 +345,13 @@ private static IEnumerable GetLength(int length) return data; } - return new[] {(byte) length}; + return new[] { (byte)length }; } - - private int ReadLength() + /// + /// Gets Data Length + /// + /// length + public int ReadLength() { int length = ReadByte(); @@ -306,12 +386,19 @@ private int ReadLength() return length; } - private void WriteBytes(IEnumerable data) + /// + /// Write Byte data into internal buffer. + /// + public void WriteBytes(IEnumerable data) { _data.AddRange(data); } - private byte ReadByte() + /// + /// Reads Byte data into internal buffer. + /// + /// data read + public byte ReadByte() { if (_readerIndex > _data.Count) throw new InvalidOperationException("Read out of boundaries."); @@ -319,7 +406,12 @@ private byte ReadByte() return _data[_readerIndex++]; } - private byte[] ReadBytes(int length) + /// + /// Reads lengths Bytes data into internal buffer. + /// + /// data read + /// amount of data to read. + public byte[] ReadBytes(int length) { if (_readerIndex + length > _data.Count) throw new InvalidOperationException("Read out of boundaries."); @@ -330,4 +422,4 @@ private byte[] ReadBytes(int length) return result; } } -} +} \ No newline at end of file diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs index 784139ab3..b2c2e7d75 100644 --- a/src/Renci.SshNet/Common/Extensions.cs +++ b/src/Renci.SshNet/Common/Extensions.cs @@ -261,6 +261,20 @@ public static byte[] TrimLeadingZeros(this byte[] value) return value; } + /// + /// Pads with leading zeros if needd + /// + /// The data. + /// The length to pad to. + public static byte[] Pad(this byte[] data, int length) + { + if (length <= data.Length) + return data; + var newData = new byte[length]; + Buffer.BlockCopy(data, 0, newData, newData.Length - data.Length, data.Length); + return newData; + } + public static byte[] Concat(this byte[] first, byte[] second) { if (first == null || first.Length == 0) diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index a8317909a..26ca884df 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -378,9 +378,13 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy HostKeyAlgorithms = new Dictionary> { +#if FEATURE_ECDSA + {"ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data)}, + {"ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data)}, + {"ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data)}, +#endif {"ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data)}, {"ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data)}, - //{"ecdsa-sha2-nistp256 "} //{"x509v3-sign-rsa", () => { ... }, //{"x509v3-sign-dss", () => { ... }, //{"spki-sign-rsa", () => { ... }, diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs index f44672347..83834d80b 100644 --- a/src/Renci.SshNet/PrivateKeyFile.cs +++ b/src/Renci.SshNet/PrivateKeyFile.cs @@ -22,7 +22,18 @@ namespace Renci.SshNet /// /// /// - /// Supports RSA and DSA private key in both OpenSSH and ssh.com format. + /// The following private keys are supported: + /// + /// + /// RSA in OpenSSH and ssh.com format + /// + /// + /// DSA in OpenSSH and ssh.com format + /// + /// + /// ECDSA 256/384/521 in OpenSSH format + /// + /// /// /// /// The following encryption algorithms are supported: @@ -197,6 +208,12 @@ private void Open(Stream privateKey, string passPhrase) _key = new DsaKey(decryptedData); HostKey = new KeyHostAlgorithm("ssh-dss", _key); break; +#if FEATURE_ECDSA + case "EC": + _key = new EcdsaKey(decryptedData); + HostKey = new KeyHostAlgorithm(_key.ToString(), _key); + break; +#endif case "SSH2 ENCRYPTED": var reader = new SshDataReader(decryptedData); var magicNumber = reader.ReadUInt32(); diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 625080160..25d882f60 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -18,7 +18,7 @@ full false bin\Debug\ - TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;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_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII + TRACE;DEBUG;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;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_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA prompt 4 bin\Debug\Renci.SshNet.xml @@ -29,7 +29,7 @@ none true bin\Release\ - FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;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_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII + FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;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_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA prompt 4 bin\Release\Renci.SshNet.xml @@ -301,6 +301,8 @@ + + diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs new file mode 100644 index 000000000..38d60966e --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs @@ -0,0 +1,189 @@ +#if FEATURE_ECDSA +using System; +using Renci.SshNet.Common; +using System.Globalization; +using System.Security.Cryptography; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Implements ECDSA digital signature algorithm. + /// + public class EcdsaDigitalSignature : DigitalSignature, IDisposable + { + private readonly EcdsaKey _key; + + /// + /// Initializes a new instance of the class. + /// + /// The ECDSA key. + /// is null. + public EcdsaDigitalSignature(EcdsaKey key) + { + if (key == null) + throw new ArgumentNullException("key"); + + _key = key; + } + + /// + /// Verifies the signature. + /// + /// The input. + /// The signature. + /// + /// true if signature was successfully verified; otherwise false. + /// + public override bool Verify(byte[] input, byte[] signature) + { + // for 521 sig_size is 132 + var sig_size = _key.KeyLength == 521 ? 132 : _key.KeyLength / 4; + var ssh_data = new SshDataSignature(signature, sig_size); +#if NETSTANDARD2_0 + return _key.Ecdsa.VerifyData(input, ssh_data.Signature, _key.HashAlgorithm); +#else + var ecdsa = (ECDsaCng)_key.Ecdsa; + ecdsa.HashAlgorithm = _key.HashAlgorithm; + return ecdsa.VerifyData(input, ssh_data.Signature); +#endif + } + + /// + /// Creates the signature. + /// + /// The input. + /// + /// Signed input data. + /// + public override byte[] Sign(byte[] input) + { +#if NETSTANDARD2_0 + var signed = _key.Ecdsa.SignData(input, _key.HashAlgorithm); +#else + var ecdsa = (ECDsaCng)_key.Ecdsa; + ecdsa.HashAlgorithm = _key.HashAlgorithm; + var signed = ecdsa.SignData(input); +#endif + var ssh_data = new SshDataSignature(signed.Length); + ssh_data.Signature = signed; + return ssh_data.GetBytes(); + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + return; + + if (disposing) + { + _isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~EcdsaDigitalSignature() + { + Dispose(false); + } + + #endregion + } + + class SshDataSignature : SshData + { + private int _signature_size; + + private byte[] _signature_r; + private byte[] _signature_s; + + public byte[] Signature + { + get + { + var signature = new byte[_signature_size]; + Buffer.BlockCopy(_signature_r, 0, signature, 0, _signature_r.Length); + Buffer.BlockCopy(_signature_s, 0, signature, _signature_r.Length, _signature_s.Length); + return signature; + } + set + { + var signed_r = new byte[_signature_size / 2]; + Buffer.BlockCopy(value, 0, signed_r, 0, signed_r.Length); + _signature_r = signed_r.ToBigInteger2().ToByteArray().Reverse(); + + var signed_s = new byte[_signature_size / 2]; + Buffer.BlockCopy(value, signed_r.Length, signed_s, 0, signed_s.Length); + _signature_s = signed_s.ToBigInteger2().ToByteArray().Reverse(); + } + } + + public SshDataSignature(int sig_size) + { + _signature_size = sig_size; + } + + public SshDataSignature(byte[] data, int sig_size) + { + _signature_size = sig_size; + Load(data); + } + + protected override void LoadData() + { + _signature_r = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2); + _signature_s = ReadBinary().TrimLeadingZeros().Pad(_signature_size / 2); + } + + protected override void SaveData() + { + WriteBinaryString(_signature_r.ToBigInteger2().ToByteArray().Reverse()); + WriteBinaryString(_signature_s.ToBigInteger2().ToByteArray().Reverse()); + } + + public new byte[] ReadBinary() + { + var length = ReadUInt32(); + + if (length > int.MaxValue) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Strings longer than {0} is not supported.", int.MaxValue)); + } + + return ReadBytes((int)length); + } + + protected override int BufferCapacity + { + get + { + var capacity = base.BufferCapacity; + capacity += 4; // r length + capacity += _signature_r.Length; // signature r + capacity += 4; // s length + capacity += _signature_s.Length; // signature s + return capacity; + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs new file mode 100644 index 000000000..58861f020 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs @@ -0,0 +1,459 @@ +#if FEATURE_ECDSA +using System; +using System.IO; +using System.Text; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Security +{ + /// + /// Contains ECDSA (ecdsa-sha2-nistp{256,384,521}) private and public key + /// + public class EcdsaKey : Key, IDisposable + { + internal const string ECDSA_P256_OID_VALUE = "1.2.840.10045.3.1.7"; // Also called nistP256 or secP256r1 + internal const string ECDSA_P384_OID_VALUE = "1.3.132.0.34"; // Also called nistP384 or secP384r1 + internal const string ECDSA_P521_OID_VALUE = "1.3.132.0.35"; // Also called nistP521or secP521r1 + +#if !NETSTANDARD2_0 + internal enum KeyBlobMagicNumber : int + { + BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345, + BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345, + BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345, + BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345, + BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345, + BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345, + + BCRYPT_ECDH_PUBLIC_P256_MAGIC = 0x314B4345, + BCRYPT_ECDH_PRIVATE_P256_MAGIC = 0x324B4345, + BCRYPT_ECDH_PUBLIC_P384_MAGIC = 0x334B4345, + BCRYPT_ECDH_PRIVATE_P384_MAGIC = 0x344B4345, + BCRYPT_ECDH_PUBLIC_P521_MAGIC = 0x354B4345, + BCRYPT_ECDH_PRIVATE_P521_MAGIC = 0x364B4345, + + BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC = 0x504B4345, + BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC = 0x564B4345, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct BCRYPT_ECCKEY_BLOB + { + internal KeyBlobMagicNumber Magic; + internal int cbKey; + } + + private CngKey key; +#endif + + /// + /// Gets the SSH name of the ECDSA Key + /// + public override string ToString() + { + return string.Format("ecdsa-sha2-nistp{0}", KeyLength); + } + +#if NETSTANDARD2_0 + /// + /// Gets the HashAlgorithm to use + /// + public HashAlgorithmName HashAlgorithm + { + get + { + switch (KeyLength) + { + case 256: + return HashAlgorithmName.SHA256; + case 384: + return HashAlgorithmName.SHA384; + case 521: + return HashAlgorithmName.SHA512; + } + return HashAlgorithmName.SHA256; + } + } +#else + /// + /// Gets the HashAlgorithm to use + /// + public CngAlgorithm HashAlgorithm + { + get + { + switch (Ecdsa.KeySize) + { + case 256: + return CngAlgorithm.Sha256; + case 384: + return CngAlgorithm.Sha384; + case 521: + return CngAlgorithm.Sha512; + default: + throw new SshException("Unknown KeySize: " + Ecdsa.KeySize); + } + } + } +#endif + + /// + /// Gets the length of the key. + /// + /// + /// The length of the key. + /// + public override int KeyLength + { + get + { + return Ecdsa.KeySize; + } + } + + private EcdsaDigitalSignature _digitalSignature; + + /// + /// Gets the digital signature. + /// + protected override DigitalSignature DigitalSignature + { + get + { + if (_digitalSignature == null) + { + _digitalSignature = new EcdsaDigitalSignature(this); + } + return _digitalSignature; + } + } + + /// + /// Gets or sets the public. + /// + /// + /// The public. + /// + public override BigInteger[] Public + { + get + { + byte[] curve; + byte[] qx; + byte[] qy; +#if NETSTANDARD2_0 + var parameter = Ecdsa.ExportParameters(false); + qx = parameter.Q.X; + qy = parameter.Q.Y; + switch (parameter.Curve.Oid.FriendlyName) + { + case "ECDSA_P256": + case "nistP256": + curve = Encoding.ASCII.GetBytes("nistp256"); + break; + case "ECDSA_P384": + case "nistP384": + curve = Encoding.ASCII.GetBytes("nistp384"); + break; + case "ECDSA_P521": + case "nistP521": + curve = Encoding.ASCII.GetBytes("nistp521"); + break; + default: + throw new SshException("Unexpected Curve Name: " + parameter.Curve.Oid.FriendlyName); + } +#else + var blob = key.Export(CngKeyBlobFormat.EccPublicBlob); + + KeyBlobMagicNumber magic; + using (var br = new BinaryReader(new MemoryStream(blob))) + { + magic = (KeyBlobMagicNumber)br.ReadInt32(); + int cbKey = br.ReadInt32(); + qx = br.ReadBytes(cbKey); + qy = br.ReadBytes(cbKey); + } + + switch (magic) + { + case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC: + curve = Encoding.ASCII.GetBytes("nistp256"); + break; + case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC: + curve = Encoding.ASCII.GetBytes("nistp384"); + break; + case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC: + curve = Encoding.ASCII.GetBytes("nistp521"); + break; + default: + throw new SshException("Unexpected Curve Magic: " + magic); + } +#endif + // Make ECPoint from x and y + // Prepend 04 (uncompressed format) + qx-bytes + qy-bytes + var q = new byte[1 + qx.Length + qy.Length]; + Buffer.SetByte(q, 0, 4); + Buffer.BlockCopy(qx, 0, q, 1, qx.Length); + Buffer.BlockCopy(qy, 0, q, qx.Length + 1, qy.Length); + + // returns Curve-Name and x/y as ECPoint + return new[] { new BigInteger(curve.Reverse()), new BigInteger(q.Reverse()) }; + } + set + { + var curve_s = Encoding.ASCII.GetString(value[0].ToByteArray().Reverse()); + string curve_oid = GetCurveOid(curve_s); + + var publickey = value[1].ToByteArray().Reverse(); + Import(curve_oid, publickey, null); + } + } + + /// + /// Gets ECDsa Object + /// + public ECDsa Ecdsa { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public EcdsaKey() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The curve name + /// Value of publickey + /// Value of privatekey + public EcdsaKey(string curve, byte[] publickey, byte[] privatekey) + { + Import(GetCurveOid(curve), publickey, privatekey); + } + + /// + /// Initializes a new instance of the class. + /// + /// DER encoded private key data. + public EcdsaKey(byte[] data) + { + var der = new DerData(data); + var version = der.ReadBigInteger(); // skip version + + // PrivateKey + var privatekey = der.ReadOctetString().TrimLeadingZeros(); + + // Construct + var s0 = der.ReadByte(); + if ((s0 & 0xe0) != 0xa0) + throw new SshException(string.Format("UnexpectedDER: wanted constructed tag (0xa0-0xbf), got: {0:X}", s0)); + var tag = s0 & 0x1f; + if (tag != 0) + throw new SshException(string.Format("expected tag 0 in DER privkey, got: {0}", tag)); + var construct = der.ReadBytes(der.ReadLength()); // object length + + // curve OID + var curve_der = new DerData(construct, true); + var curve = curve_der.ReadObject(); + + // Construct + s0 = der.ReadByte(); + if ((s0 & 0xe0) != 0xa0) + throw new SshException(string.Format("UnexpectedDER: wanted constructed tag (0xa0-0xbf), got: {0:X}", s0)); + tag = s0 & 0x1f; + if (tag != 1) + throw new SshException(string.Format("expected tag 1 in DER privkey, got: {0}", tag)); + construct = der.ReadBytes(der.ReadLength()); // object length + + // PublicKey + var pubkey_der = new DerData(construct, true); + var pubkey = pubkey_der.ReadBitString().TrimLeadingZeros(); + + Import(OidByteArrayToString(curve), pubkey, privatekey); + } + + private void Import(string curve_oid, byte[] publickey, byte[] privatekey) + { +#if NETSTANDARD2_0 + var curve = ECCurve.CreateFromValue(curve_oid); + var parameter = new ECParameters + { + Curve = curve + }; + + // ECPoint as BigInteger(2) + var cord_size = (publickey.Length - 1) / 2; + var qx = new byte[cord_size]; + Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length); + + var qy = new byte[cord_size]; + Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length); + + parameter.Q.X = qx; + parameter.Q.Y = qy; + + if (privatekey != null) + parameter.D = privatekey.TrimLeadingZeros().Pad(cord_size); + + Ecdsa = ECDsa.Create(parameter); +#else + var curve_magic = KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC; + switch (GetCurveName(curve_oid)) + { + case "nistp256": + if (privatekey != null) + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P256_MAGIC; + else + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC; + break; + case "nistp384": + if (privatekey != null) + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P384_MAGIC; + else + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC; + break; + case "nistp521": + if (privatekey != null) + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P521_MAGIC; + else + curve_magic = KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC; + break; + default: + throw new SshException("Unknown: " + curve_oid); + } + + // ECPoint as BigInteger(2) + var cord_size = (publickey.Length - 1) / 2; + var qx = new byte[cord_size]; + Buffer.BlockCopy(publickey, 1, qx, 0, qx.Length); + + var qy = new byte[cord_size]; + Buffer.BlockCopy(publickey, cord_size + 1, qy, 0, qy.Length); + + if (privatekey != null) + privatekey = privatekey.Pad(cord_size); + + int headerSize = Marshal.SizeOf(typeof(BCRYPT_ECCKEY_BLOB)); + int blobSize = headerSize + qx.Length + qy.Length; + if (privatekey != null) + blobSize += privatekey.Length; + + byte[] blob = new byte[blobSize]; + using (var bw = new BinaryWriter(new MemoryStream(blob))) + { + bw.Write((int)curve_magic); + bw.Write(cord_size); + bw.Write(qx); // q.x + bw.Write(qy); // q.y + if (privatekey != null) + bw.Write(privatekey); // d + } + key = CngKey.Import(blob, privatekey == null ? CngKeyBlobFormat.EccPublicBlob : CngKeyBlobFormat.EccPrivateBlob); + + Ecdsa = new ECDsaCng(key); +#endif + } + + private string GetCurveOid(string curve_s) + { + switch (curve_s.ToLower()) + { + case "nistp256": + return ECDSA_P256_OID_VALUE; + case "nistp384": + return ECDSA_P384_OID_VALUE; + case "nistp521": + return ECDSA_P521_OID_VALUE; + default: + throw new SshException("Unexpected Curve Name: " + curve_s); + } + } + + private string GetCurveName(string oid) + { + switch (oid) + { + case ECDSA_P256_OID_VALUE: + return "nistp256"; + case ECDSA_P384_OID_VALUE: + return "nistp384"; + case ECDSA_P521_OID_VALUE: + return "nistp521"; + default: + throw new SshException("Unexpected OID: " + oid); + } + } + + private string OidByteArrayToString(byte[] oid) + { + StringBuilder retVal = new StringBuilder(); + + for (int i = 0; i < oid.Length; i++) + { + if (i == 0) + { + int b = oid[0] % 40; + int a = (oid[0] - b) / 40; + retVal.AppendFormat("{0}.{1}", a, b); + } + else + { + if (oid[i] < 128) + retVal.AppendFormat(".{0}", oid[i]); + else + { + retVal.AppendFormat(".{0}", + ((oid[i] - 128) * 128) + oid[i + 1]); + i++; + } + } + } + + return retVal.ToString(); + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + return; + + if (disposing) + { + _isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~EcdsaKey() + { + Dispose(false); + } + + #endregion + } +} +#endif \ No newline at end of file From c9f9e3ca35cb2ec47a6e38bdc246b23e8fe19359 Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Tue, 27 Nov 2018 15:46:59 +0100 Subject: [PATCH 6/8] Support for Ed25519 Host- and Private-Keys --- THIRD-PARTY-NOTICES.TXT | 18 + .../Renci.SshNet.NET35.csproj | 3 + .../Classes/PrivateKeyFileTest.cs | 30 +- .../Data/Key.OPENSSH.ED25519.Encrypted.txt | 9 + .../Data/Key.OPENSSH.ED25519.txt | 8 + .../Renci.SshNet.Tests.csproj | 4 + src/Renci.SshNet/ConnectionInfo.cs | 1 + src/Renci.SshNet/PrivateKeyFile.cs | 157 ++- src/Renci.SshNet/Renci.SshNet.csproj | 5 +- .../Security/Cryptography/Bcrypt.cs | 956 ++++++++++++++++++ .../Cryptography/ED25519DigitalSignature.cs | 93 ++ .../Security/Cryptography/ED25519Key.cs | 154 +++ src/Renci.SshNet/Security/KeyHostAlgorithm.cs | 2 +- 13 files changed, 1434 insertions(+), 6 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt create mode 100644 src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt create mode 100644 src/Renci.SshNet/Security/Cryptography/Bcrypt.cs create mode 100644 src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs create mode 100644 src/Renci.SshNet/Security/Cryptography/ED25519Key.cs diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index 55630a8e9..1cacf79e2 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -53,3 +53,21 @@ SHA512 written by Christian Winnerlein (CodesInChaos) public domain directly from the specification + +License notice for BCrypt +------------------------- + +Copyright (c) 2006 Damien Miller +Copyright (c) 2010 Ryan D. Emerle + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj index 0df2f4f50..0d83005e2 100644 --- a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj +++ b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj @@ -824,6 +824,9 @@ Security\Cryptography\AsymmetricCipher.cs + + Security\Cryptography\Bcrypt.cs + Security\Cryptography\BlockCipher.cs diff --git a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs index 3c944c8d4..99434d71a 100644 --- a/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs +++ b/src/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs @@ -545,6 +545,34 @@ public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsShar } } + /// + /// A test for opening an openssh v1 keyfile where there is no passphrase. + /// + [TestMethod()] + [Owner("bhalbright")] + [TestCategory("PrivateKey")] + public void TestOpenSshV1KeyFileNoPassphrase() + { + using (var stream = GetData("Key.OPENSSH.ED25519.txt")) + { + new PrivateKeyFile(stream); + } + } + + /// + /// A test for opening an openssh v1 keyfile where there is a passphrase. + /// + [TestMethod()] + [Owner("bhalbright")] + [TestCategory("PrivateKey")] + public void TestOpenSshV1KeyFileWithPassphrase() + { + using (var stream = GetData("Key.OPENSSH.ED25519.Encrypted.txt")) + { + new PrivateKeyFile(stream, "password"); + } + } + private void SaveStreamToFile(Stream stream, string fileName) { var buffer = new byte[4000]; @@ -567,4 +595,4 @@ private string GetTempFileName() return tempFile; } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt new file mode 100644 index 000000000..50b4d9689 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABBg +HWh+J0IG6OfYxD74SoT9AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIGFd +yflleGqSPOhgSYZf7ZQFlG0zEL9VDGC69UbtaaByAAAAoDLm8u8wFwlqjzZRfVxj +wzGTYFJFtfkHRqfFBE4xKgknHNRbCT1OQb7rgE7nZbUXIlb1NCTZLbXti9AYNZpz +ycvPD4Dc6lB03b8pNHoFVSkrCwxrWB5bKtIM4OZNcDK1lZDBEWE2aZXf9puRHbu3 +ccrK/F5GjRi2pUa8qnfqThN1mNPZwFTx4oSKeRaUMdeHBrNwDtaxq32A6Q4KHoYO +KPM= +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt new file mode 100644 index 000000000..84811f653 --- /dev/null +++ b/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz +c2gtZWQyNTUxOQAAACANCWZw0K8UGXDQC32WBuyzwFtTGBBr1VuZ43uzpTBjIgAA +AKBATgCiQE4AogAAAAtzc2gtZWQyNTUxOQAAACANCWZw0K8UGXDQC32WBuyzwFtT +GBBr1VuZ43uzpTBjIgAAAEAAzBF1MPUxrs+ycpJh28zzo/F3m6WcKO+orsSbR5Lw +KQ0JZnDQrxQZcNALfZYG7LPAW1MYEGvVW5nje7OlMGMiAAAAFGVkMjU1MTkta2V5 +LTIwMTgxMTI3AQIDBAUGBwgJ +-----END OPENSSH PRIVATE KEY----- diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index f32beeed3..5b9ccafc6 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -719,6 +719,10 @@ + + + + - + \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs b/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs new file mode 100644 index 000000000..837d00318 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/Bcrypt.cs @@ -0,0 +1,956 @@ +// +// Copyright (c) 2006 Damien Miller +// Copyright (c) 2010 Ryan D. Emerle +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +using Renci.SshNet.Abstractions; +using System; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Text; + +namespace Renci.SshNet.Security.Cryptography +{ + /// BCrypt implementation. + /// + /// + /// BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in + /// "A Future- + /// Adaptable Password Scheme" by Niels Provos and David Mazieres. + /// + /// + /// This password hashing system tries to thwart off-line password cracking using a + /// computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher. + /// The work factor of the algorithm is parameterised, so it can be increased as computers + /// get faster. + /// + /// + /// Usage is really simple. To hash a password for the first time, call the method with a random salt, like this: + /// + /// string pw_hash = BCrypt.HashPassword(plain_password); + /// + /// To check whether a plaintext password matches one that has been hashed previously, + /// use the method: + /// + /// + /// if (BCrypt.Verify(candidate_password, stored_hash)) + /// Console.WriteLine("It matches"); + /// else + /// Console.WriteLine("It does not match"); + /// + /// + /// The method takes an optional parameter (workFactor) that + /// determines the computational complexity of the hashing: + /// + /// + /// string strong_salt = BCrypt.GenerateSalt(10); + /// string stronger_salt = BCrypt.GenerateSalt(12); + /// + /// + /// The amount of work increases exponentially (2^workFactor), so each increment is twice + /// as much work. The default workFactor is 10, and the valid range is 4 to 31. + /// + /// + internal sealed class BCrypt + { + // BCrypt parameters + private const int GENSALT_DEFAULT_LOG2_ROUNDS = 10; + private const int BCRYPT_SALT_LEN = 16; + + // Blowfish parameters + private const int BLOWFISH_NUM_ROUNDS = 16; + + // Initial contents of key schedule + private static readonly uint[] _POrig = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + }; + + private static readonly uint[] _SOrig = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + }; + + // OpenBSD IV: "OxychromaticBlowfishSwatDynamite" in big endian + private static readonly uint[] _OpenBsdIv = new uint[] { + 0x4f787963, 0x68726f6d, 0x61746963, 0x426c6f77, + 0x66697368, 0x53776174, 0x44796e61, 0x6d697465, + }; + + // bcrypt IV: "OrpheanBeholderScryDoubt" + private static readonly uint[] _BfCryptCiphertext = { + 0x4f727068, 0x65616e42, 0x65686f6c, + 0x64657253, 0x63727944, 0x6f756274 + }; + + // Table for Base64 encoding + private static readonly char[] _Base64Code = { + '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', + 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9' + }; + + // Table for Base64 decoding + private static readonly int[] _Index64 = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, + -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + -1, -1, -1, -1, -1, -1, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, -1, -1, -1, -1, -1 + }; + + // Expanded Blowfish key + private uint[] _P; + private uint[] _S; + + /// + /// Hash a string using the OpenBSD bcrypt scheme and a salt generated by . + /// + /// Just an alias for HashPassword. + /// The string to hash. + /// The hashed string. + public static string HashString(string source) + { + return HashPassword(source); + } + + /// + /// Hash a string using the OpenBSD bcrypt scheme and a salt generated by . + /// + /// Just an alias for HashPassword. + /// The string to hash. + /// The log2 of the number of rounds of hashing to apply - the work + /// factor therefore increases as 2^workFactor. + /// The hashed string. + public static string HashString(string source, int workFactor) + { + return HashPassword(source, GenerateSalt(workFactor)); + } + + /// + /// Hash a password using the OpenBSD bcrypt scheme and a salt generated by . + /// + /// The password to hash. + /// The hashed password. + public static string HashPassword(string input) + { + return HashPassword(input, GenerateSalt()); + } + + /// + /// Hash a password using the OpenBSD bcrypt scheme and a salt generated by using the given . + /// + /// The password to hash. + /// The log2 of the number of rounds of hashing to apply - the work + /// factor therefore increases as 2^workFactor. + /// The hashed password. + public static string HashPassword(string input, int workFactor) + { + return HashPassword(input, GenerateSalt(workFactor)); + } + + /// Hash a password using the OpenBSD bcrypt scheme. + /// Thrown when one or more arguments have unsupported or + /// illegal values. + /// The password to hash. + /// the salt to hash with (perhaps generated using BCrypt.gensalt). + /// The hashed password + public static string HashPassword(string input, string salt) + { + if (input == null) + throw new ArgumentNullException("input"); + + if (string.IsNullOrEmpty(salt)) + throw new ArgumentException("Invalid salt", "salt"); + + // Determinthe starting offset and validate the salt + int startingOffset; + char minor = (char)0; + if (salt[0] != '$' || salt[1] != '2') + throw new SaltParseException("Invalid salt version"); + if (salt[2] == '$') + startingOffset = 3; + else + { + minor = salt[2]; + if (minor != 'a' || salt[3] != '$') + throw new SaltParseException("Invalid salt revision"); + startingOffset = 4; + } + + // Extract number of rounds + if (salt[startingOffset + 2] > '$') + throw new SaltParseException("Missing salt rounds"); + + // Extract details from salt + int logRounds = Convert.ToInt32(salt.Substring(startingOffset, 2)); + string extractedSalt = salt.Substring(startingOffset + 3, 22); + + byte[] inputBytes = Encoding.UTF8.GetBytes((input + (minor >= 'a' ? "\0" : ""))); + byte[] saltBytes = DecodeBase64(extractedSalt, BCRYPT_SALT_LEN); + + BCrypt bCrypt = new BCrypt(); + byte[] hashed = bCrypt.CryptRaw(inputBytes, saltBytes, logRounds); + + // Generate result string + StringBuilder result = new StringBuilder(); + result.Append("$2"); + if (minor >= 'a') + result.Append(minor); + result.AppendFormat("${0:00}$", logRounds); + result.Append(EncodeBase64(saltBytes, saltBytes.Length)); + result.Append(EncodeBase64(hashed, (_BfCryptCiphertext.Length * 4) - 1)); + return result.ToString(); + } + + /// + /// Generate a salt for use with the method. + /// + /// The log2 of the number of rounds of hashing to apply - the work + /// factor therefore increases as 2**workFactor. + /// A base64 encoded salt value. + public static string GenerateSalt(int workFactor) + { + if (workFactor < 4 || workFactor > 31) + throw new ArgumentOutOfRangeException("workFactor", "The work factor must be between 4 and 31 (inclusive)"); + + byte[] rnd = new byte[BCRYPT_SALT_LEN]; +#if FEATURE_RNG_CREATE + RandomNumberGenerator rng = RandomNumberGenerator.Create(); +#elif FEATURE_RNG_CSP + RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); +#endif +#if FEATURE_RNG_CREATE || FEATURE_RNG_CSP + rng.GetBytes(rnd); +#endif + + StringBuilder rs = new StringBuilder(); + rs.AppendFormat("$2a${0:00}$", workFactor); + rs.Append(EncodeBase64(rnd, rnd.Length)); + return rs.ToString(); + } + + /// + /// Generate a salt for use with the method + /// selecting a reasonable default for the number of hashing rounds to apply. + /// + /// A base64 encoded salt value. + public static string GenerateSalt() + { + return GenerateSalt(GENSALT_DEFAULT_LOG2_ROUNDS); + } + + /// + /// Verifies that the hash of the given matches the provided + /// + /// + /// The text to verify. + /// The previously-hashed password. + /// true if the passwords match, false otherwise. + public static bool Verify(string text, string hash) + { + return hash == HashPassword(text, hash); + } + + /// + /// Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note that this + /// is *not* compatible with the standard MIME-base64 encoding. + /// + /// Thrown when one or more arguments have unsupported or + /// illegal values. + /// The byte array to encode. + /// The number of bytes to encode. + /// Base64-encoded string. + private static string EncodeBase64(byte[] byteArray, int length) + { + if (length <= 0 || length > byteArray.Length) + throw new ArgumentException("Invalid length", "length"); + + int off = 0; + StringBuilder rs = new StringBuilder(); + while (off < length) + { + int c1 = byteArray[off++] & 0xff; + rs.Append(_Base64Code[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= length) + { + rs.Append(_Base64Code[c1 & 0x3f]); + break; + } + int c2 = byteArray[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.Append(_Base64Code[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= length) + { + rs.Append(_Base64Code[c1 & 0x3f]); + break; + } + c2 = byteArray[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.Append(_Base64Code[c1 & 0x3f]); + rs.Append(_Base64Code[c2 & 0x3f]); + } + return rs.ToString(); + } + + /// + /// Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that this is *not* + /// compatible with the standard MIME-base64 encoding. + /// + /// Thrown when one or more arguments have unsupported or + /// illegal values. + /// The string to decode. + /// The maximum bytes to decode. + /// The decoded byte array. + private static byte[] DecodeBase64(string encodedstring, int maximumBytes) + { + int position = 0, + sourceLength = encodedstring.Length, + outputLength = 0; + + if (maximumBytes <= 0) + throw new ArgumentException("Invalid maximum bytes value", "maximumBytes"); + + // TODO: update to use a List - it's only ever 16 bytes, so it's not a big deal + StringBuilder rs = new StringBuilder(); + while (position < sourceLength - 1 && outputLength < maximumBytes) + { + int c1 = Char64(encodedstring[position++]); + int c2 = Char64(encodedstring[position++]); + if (c1 == -1 || c2 == -1) + break; + + rs.Append((char)((c1 << 2) | ((c2 & 0x30) >> 4))); + if (++outputLength >= maximumBytes || position >= sourceLength) + break; + + int c3 = Char64(encodedstring[position++]); + if (c3 == -1) + break; + + rs.Append((char)(((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2))); + if (++outputLength >= maximumBytes || position >= sourceLength) + break; + + int c4 = Char64(encodedstring[position++]); + rs.Append((char)(((c3 & 0x03) << 6) | c4)); + + ++outputLength; + } + + byte[] ret = new byte[outputLength]; + for (position = 0; position < outputLength; position++) + ret[position] = (byte)rs[position]; + return ret; + } + + /// + /// Look up the 3 bits base64-encoded by the specified character, range-checking against + /// conversion table. + /// + /// The base64-encoded value. + /// The decoded value of x. + private static int Char64(char character) + { + if (character < 0 || character > _Index64.Length) + return -1; + return _Index64[character]; + } + + /// Blowfish encipher a single 64-bit block encoded as two 32-bit halves. + /// An array containing the two 32-bit half blocks. + /// The position in the array of the blocks. + private void Encipher(uint[] blockArray, int offset) + { + uint round, + n, + block = blockArray[offset], + r = blockArray[offset + 1]; + + block ^= _P[0]; + for (round = 0; round <= BLOWFISH_NUM_ROUNDS - 2;) + { + // Feistel substitution on left word + n = _S[(block >> 24) & 0xff]; + n += _S[0x100 | ((block >> 16) & 0xff)]; + n ^= _S[0x200 | ((block >> 8) & 0xff)]; + n += _S[0x300 | (block & 0xff)]; + r ^= n ^ _P[++round]; + + // Feistel substitution on right word + n = _S[(r >> 24) & 0xff]; + n += _S[0x100 | ((r >> 16) & 0xff)]; + n ^= _S[0x200 | ((r >> 8) & 0xff)]; + n += _S[0x300 | (r & 0xff)]; + block ^= n ^ _P[++round]; + } + blockArray[offset] = r ^ _P[BLOWFISH_NUM_ROUNDS + 1]; + blockArray[offset + 1] = block; + } + + /// Cycically extract a word of key material. + /// The string to extract the data from. + /// [in,out] The current offset. + /// The next word of material from data. + private static uint StreamToWord(byte[] data, ref int offset) + { + int i; + uint word = 0; + + for (i = 0; i < 4; i++) + { + word = (word << 8) | (uint)(data[offset] & 0xff); + offset = (offset + 1) % data.Length; + } + return word; + } + + /// Initializes the Blowfish key schedule. + private void InitializeKey() + { + _P = new uint[_POrig.Length]; + _S = new uint[_SOrig.Length]; + Array.Copy(_POrig, _P, _POrig.Length); + Array.Copy(_SOrig, _S, _SOrig.Length); + } + + /// Key the Blowfish cipher. + /// The key byte array. + private void Key(byte[] keyBytes) + { + int i; + int koffp = 0; + uint[] lr = { 0, 0 }; + int plen = _P.Length, slen = _S.Length; + + for (i = 0; i < plen; i++) + _P[i] = _P[i] ^ StreamToWord(keyBytes, ref koffp); + + for (i = 0; i < plen; i += 2) + { + Encipher(lr, 0); + _P[i] = lr[0]; + _P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) + { + Encipher(lr, 0); + _S[i] = lr[0]; + _S[i + 1] = lr[1]; + } + } + + /// + /// Perform the "enhanced key schedule" step described by Provos and Mazieres in "A Future- + /// Adaptable Password Scheme" http://www.openbsd.org/papers/bcrypt-paper.ps. + /// + /// Salt byte array. + /// Input byte array. + private void EKSKey(byte[] saltBytes, byte[] inputBytes) + { + int i; + int passwordOffset = 0, + saltOffset = 0; + uint[] lr = { 0, 0 }; + int plen = _P.Length, slen = _S.Length; + + for (i = 0; i < plen; i++) + _P[i] = _P[i] ^ StreamToWord(inputBytes, ref passwordOffset); + + for (i = 0; i < plen; i += 2) + { + lr[0] ^= StreamToWord(saltBytes, ref saltOffset); + lr[1] ^= StreamToWord(saltBytes, ref saltOffset); + Encipher(lr, 0); + _P[i] = lr[0]; + _P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) + { + lr[0] ^= StreamToWord(saltBytes, ref saltOffset); + lr[1] ^= StreamToWord(saltBytes, ref saltOffset); + Encipher(lr, 0); + _S[i] = lr[0]; + _S[i + 1] = lr[1]; + } + } + + /// Perform the central hashing step in the bcrypt scheme. + /// Thrown when one or more arguments have unsupported or + /// illegal values. + /// The input byte array to hash. + /// The salt byte array to hash with. + /// The binary logarithm of the number of rounds of hashing to apply. + /// A byte array containing the hashed result. + private byte[] CryptRaw(byte[] inputBytes, byte[] saltBytes, int logRounds) + { + uint[] cdata = new uint[_BfCryptCiphertext.Length]; + Array.Copy(_BfCryptCiphertext, cdata, _BfCryptCiphertext.Length); + int clen = cdata.Length; + + if (logRounds < 4 || logRounds > 31) + throw new ArgumentException("Bad number of rounds", "logRounds"); + + if (saltBytes.Length != BCRYPT_SALT_LEN) + throw new ArgumentException("Bad salt Length", "saltBytes"); + + uint rounds = 1u << logRounds; + Debug.Assert(rounds > 0, "Rounds must be > 0"); // We overflowed rounds at 31 - added safety check + + InitializeKey(); + EKSKey(saltBytes, inputBytes); + + for (int i = 0; i < rounds; i++) + { + Key(inputBytes); + Key(saltBytes); + } + + for (int i = 0; i < 64; i++) + { + for (int j = 0; j < (clen >> 1); j++) + Encipher(cdata, j << 1); + } + + byte[] ret = new byte[clen * 4]; + for (int i = 0, j = 0; i < clen; i++) + { + ret[j++] = (byte)((cdata[i] >> 24) & 0xff); + ret[j++] = (byte)((cdata[i] >> 16) & 0xff); + ret[j++] = (byte)((cdata[i] >> 8) & 0xff); + ret[j++] = (byte)(cdata[i] & 0xff); + } + return ret; + } + + /** + * Compatibility with new OpenBSD function. + * Ported from SSHJ library (https://github.com/hierynomus/sshj) + */ + public void Hash(byte[] hpass, byte[] hsalt, byte[] output) + { + InitializeKey(); + EKSKey(hsalt, hpass); + for (int i = 0; i < 64; i++) + { + Key(hsalt); + Key(hpass); + } + + uint[] buf = new uint[_OpenBsdIv.Length]; + Array.Copy(_OpenBsdIv, 0, buf, 0, _OpenBsdIv.Length); + for (int i = 0; i < 8; i += 2) + { + for (int j = 0; j < 64; j++) + { + Encipher(buf, i); + } + } + + for (int i = 0, j = 0; i < buf.Length; i++) + { + // Output of this is little endian + output[j++] = (byte)(buf[i] & 0xff); + output[j++] = (byte)((buf[i] >> 8) & 0xff); + output[j++] = (byte)((buf[i] >> 16) & 0xff); + output[j++] = (byte)((buf[i] >> 24) & 0xff); + } + } + + /// + /// Applies the Bcrypt kdf to derive a key and iv from the passphrase, + /// the key/iv are returned in the output variable. + /// Ported from the SSHJ library. https://github.com/hierynomus/sshj + /// + /// + /// + /// + /// + public void Pbkdf(byte[] password, byte[] salt, int rounds, byte[] output) + { + using (var sha512 = CryptoAbstraction.CreateSHA512()) + { + int nblocks = (output.Length + 31) / 32; + byte[] hpass = sha512.ComputeHash(password); + + byte[] hsalt = new byte[64]; + byte[] block_b = new byte[4]; + byte[] outBytes = new byte[32]; + byte[] tmp = new byte[32]; + for (int block = 1; block <= nblocks; block++) + { + // Block count is in big endian + block_b[0] = (byte)((block >> 24) & 0xFF); + block_b[1] = (byte)((block >> 16) & 0xFF); + block_b[2] = (byte)((block >> 8) & 0xFF); + block_b[3] = (byte)(block & 0xFF); + + hsalt = sha512.ComputeHash(AppendArrays(salt, block_b)); + Hash(hpass, hsalt, outBytes); + Array.Copy(outBytes, 0, tmp, 0, outBytes.Length); + + for (int round = 1; round < rounds; round++) + { + hsalt = sha512.ComputeHash(tmp); + Hash(hpass, hsalt, tmp); + + for (int i = 0; i < tmp.Length; i++) + { + outBytes[i] ^= tmp[i]; + } + } + + for (int i = 0; i < outBytes.Length; i++) + { + int idx = i * nblocks + (block - 1); + if (idx < output.Length) + { + output[idx] = outBytes[i]; + } + } + } + } + } + + /// + /// Appends multiple byte arrays into one array. + /// + /// + /// + /// + private byte[] AppendArrays(byte[] b1, params byte[][] others) + { + //from https://stackoverflow.com/questions/46534429/how-to-convert-java-messagedigest-with-two-update-statements-to-c-sharp + int n = b1.Length; + foreach (var other in others) + { + n += other.Length; + } + + var result = new byte[n]; + + n = 0; + Array.Copy(b1, 0, result, n, b1.Length); + n += b1.Length; + foreach (var other in others) + { + Array.Copy(other, 0, result, n, other.Length); + n += other.Length; + } + + return result; + } + + /// Exception for signalling parse errors. + public class SaltParseException : Exception + { + /// Default constructor. + public SaltParseException() + { + } + + /// Initializes a new instance of . + /// The message. + public SaltParseException(string message) + : base(message) + { + } + + /// Initializes a new instance of . + /// The message. + /// The inner exception. + public SaltParseException(string message, Exception innerException) + : base(message, innerException) + { + } + } + } +} diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs b/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs new file mode 100644 index 000000000..be68fd481 --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/ED25519DigitalSignature.cs @@ -0,0 +1,93 @@ +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Chaos.NaCl; + +namespace Renci.SshNet.Security.Cryptography +{ + /// + /// Implements ECDSA digital signature algorithm. + /// + public class ED25519DigitalSignature : DigitalSignature, IDisposable + { + private readonly ED25519Key _key; + + /// + /// Initializes a new instance of the class. + /// + /// The ED25519Key key. + /// is null. + public ED25519DigitalSignature(ED25519Key key) + { + if (key == null) + throw new ArgumentNullException("key"); + + _key = key; + } + + /// + /// Verifies the signature. + /// + /// The input. + /// The signature. + /// + /// true if signature was successfully verified; otherwise false. + /// + /// Invalid signature. + public override bool Verify(byte[] input, byte[] signature) + { + return Ed25519.Verify(signature, input, _key.PublicKey); + } + + /// + /// Creates the signature. + /// + /// The input. + /// + /// Signed input data. + /// + /// Invalid ED25519Key key. + public override byte[] Sign(byte[] input) + { + return Ed25519.Sign(input, _key.PrivateKey); + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + return; + + if (disposing) + { + _isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~ED25519DigitalSignature() + { + Dispose(false); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs new file mode 100644 index 000000000..83ac1c8cd --- /dev/null +++ b/src/Renci.SshNet/Security/Cryptography/ED25519Key.cs @@ -0,0 +1,154 @@ +using System; +using Renci.SshNet.Common; +using Renci.SshNet.Security.Cryptography; +using Renci.SshNet.Security.Chaos.NaCl; + +namespace Renci.SshNet.Security +{ + /// + /// Contains ED25519 private and public key + /// + public class ED25519Key : Key, IDisposable + { + private ED25519DigitalSignature _digitalSignature; + + private byte[] publicKey = new byte[Ed25519.PublicKeySizeInBytes]; + private byte[] privateKey = new byte[Ed25519.ExpandedPrivateKeySizeInBytes]; + + /// + /// Gets the Key String. + /// + public override string ToString() + { + return "ssh-ed25519"; + } + + /// + /// Gets or sets the public. + /// + /// + /// The public. + /// + public override BigInteger[] Public + { + get + { + return new BigInteger[] { publicKey.ToBigInteger() }; + } + set + { + publicKey = value[0].ToByteArray().Reverse().TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + } + } + + /// + /// Gets the length of the key. + /// + /// + /// The length of the key. + /// + public override int KeyLength + { + get + { + return PublicKey.Length; + } + } + + /// + /// Gets the digital signature. + /// + protected override DigitalSignature DigitalSignature + { + get + { + if (_digitalSignature == null) + { + _digitalSignature = new ED25519DigitalSignature(this); + } + return _digitalSignature; + } + } + + /// + /// Gets the PublicKey Bytes + /// + public byte[] PublicKey + { + get + { + return publicKey; + } + } + + /// + /// Gets the PrivateKey Bytes + /// + public byte[] PrivateKey + { + get + { + return privateKey; + } + } + + /// + /// Initializes a new instance of the class. + /// + public ED25519Key() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// pk data. + /// sk data. + public ED25519Key(byte[] pk, byte[] sk) + { + publicKey = pk.TrimLeadingZeros().Pad(Ed25519.PublicKeySizeInBytes); + var seed = new byte[Ed25519.PrivateKeySeedSizeInBytes]; + Buffer.BlockCopy(sk, 0, seed, 0, seed.Length); + Ed25519.KeyPairFromSeed(out publicKey, out privateKey, seed); + } + + #region IDisposable Members + + private bool _isDisposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_isDisposed) + return; + + if (disposing) + { + _isDisposed = true; + } + } + + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~ED25519Key() + { + Dispose(false); + } + + #endregion + } +} diff --git a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs index 619bfc922..e98992147 100644 --- a/src/Renci.SshNet/Security/KeyHostAlgorithm.cs +++ b/src/Renci.SshNet/Security/KeyHostAlgorithm.cs @@ -92,7 +92,7 @@ public BigInteger[] Keys for (var i = 0; i < _keys.Count; i++) { var key = _keys[i]; - keys[i] = key.ToBigInteger(); + keys[i] = key.ToBigInteger2(); } return keys; } From 7d7365156eeb4c7d22ca6906a683a701f0bdca6f Mon Sep 17 00:00:00 2001 From: Stefan Rinkes Date: Mon, 3 Dec 2018 09:13:24 +0100 Subject: [PATCH 7/8] Enable Build of Elliptic-Curves for other Projects --- .../Renci.SshNet.NET35.csproj | 207 +++++++++++++++++ .../Renci.SshNet.Silverlight5.csproj | 210 +++++++++++++++++ .../Renci.SshNet.Tests.NET35.csproj | 6 + .../Renci.SshNet.UAP10.csproj | 216 ++++++++++++++++++ .../Renci.SshNet.WindowsPhone8.csproj | 216 ++++++++++++++++++ .../crypto/prng/CryptoApiRandomGenerator.cs | 4 - .../Security/BouncyCastle/math/BigInteger.cs | 2 +- .../security/SecurityUtilityException.cs | 2 +- 8 files changed, 857 insertions(+), 6 deletions(-) diff --git a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj index 0d83005e2..ffd2bce5b 100644 --- a/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj +++ b/src/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj @@ -821,6 +821,189 @@ Security\CertificateHostAlgorithm.cs + + Security\Cryptography\Chaos.NaCl\CryptoBytes.cs + + + Security\Cryptography\Chaos.NaCl\Ed25519.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array16.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array8.cs + + + Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs + + + Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs + + + Security\Cryptography\Chaos.NaCl\Sha512.cs + Security\Cryptography\AsymmetricCipher.cs @@ -896,6 +1079,12 @@ Security\Cryptography\DsaKey.cs + + Security\Cryptography\ED25519DigitalSignature.cs + + + Security\Cryptography\ED25519Key.cs + Security\Cryptography\HMACMD5.cs @@ -965,6 +1154,24 @@ Security\KeyExchangeDiffieHellmanGroupSha1.cs + + Security\KeyExchangeEC.cs + + + Security\KeyExchangeECCurve25519.cs + + + Security\KeyExchangeECDH.cs + + + Security\KeyExchangeECDH256.cs + + + Security\KeyExchangeECDH384.cs + + + Security\KeyExchangeECDH521.cs + Security\KeyHostAlgorithm.cs diff --git a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj b/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj index e66276007..2523a02f3 100644 --- a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj +++ b/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj @@ -821,9 +821,195 @@ Security\CertificateHostAlgorithm.cs + + Security\Cryptography\Chaos.NaCl\CryptoBytes.cs + + + Security\Cryptography\Chaos.NaCl\Ed25519.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array16.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array8.cs + + + Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs + + + Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs + + + Security\Cryptography\Chaos.NaCl\Sha512.cs + Security\Cryptography\AsymmetricCipher.cs + + Security\Cryptography\Bcrypt.cs + Security\Cryptography\BlockCipher.cs @@ -893,6 +1079,12 @@ Security\Cryptography\DsaKey.cs + + Security\Cryptography\ED25519DigitalSignature.cs + + + Security\Cryptography\ED25519Key.cs + Security\Cryptography\HMACMD5.cs @@ -956,6 +1148,24 @@ Security\KeyExchangeDiffieHellmanGroupSha1.cs + + Security\KeyExchangeEC.cs + + + Security\KeyExchangeECCurve25519.cs + + + Security\KeyExchangeECDH.cs + + + Security\KeyExchangeECDH256.cs + + + Security\KeyExchangeECDH384.cs + + + Security\KeyExchangeECDH521.cs + Security\KeyHostAlgorithm.cs diff --git a/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj b/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj index ec5feb95e..abeeb691f 100644 --- a/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj +++ b/src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj @@ -1762,6 +1762,12 @@ Data\Key.ECDSA521.Encrypted.txt + + Data\Key.OPENSSH.ED25519.Encrypted.txt + + + Data\Key.OPENSSH.ED25519.txt + diff --git a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj b/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj index 369c3da7d..d1b874f42 100644 --- a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj +++ b/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj @@ -885,9 +885,195 @@ Security\CertificateHostAlgorithm.cs + + Security\Cryptography\Chaos.NaCl\CryptoBytes.cs + + + Security\Cryptography\Chaos.NaCl\Ed25519.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array16.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array8.cs + + + Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs + + + Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs + + + Security\Cryptography\Chaos.NaCl\Sha512.cs + Security\Cryptography\AsymmetricCipher.cs + + Security\Cryptography\Bcrypt.cs + Security\Cryptography\BlockCipher.cs @@ -957,6 +1143,12 @@ Security\Cryptography\DsaKey.cs + + Security\Cryptography\ED25519DigitalSignature.cs + + + Security\Cryptography\ED25519Key.cs + Security\Cryptography\HMACMD5.cs @@ -975,6 +1167,12 @@ Security\Cryptography\Key.cs + + Security\Cryptography\EcdsaDigitalSignature.cs + + + Security\Cryptography\EcdsaKey.cs + Security\Cryptography\RsaDigitalSignature.cs @@ -1020,6 +1218,24 @@ Security\KeyExchangeDiffieHellmanGroupSha1.cs + + Security\KeyExchangeEC.cs + + + Security\KeyExchangeECCurve25519.cs + + + Security\KeyExchangeECDH.cs + + + Security\KeyExchangeECDH256.cs + + + Security\KeyExchangeECDH384.cs + + + Security\KeyExchangeECDH521.cs + Security\KeyHostAlgorithm.cs diff --git a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj b/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj index 9300a9b41..7a915c18b 100644 --- a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj +++ b/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj @@ -850,9 +850,195 @@ Security\CertificateHostAlgorithm.cs + + Security\Cryptography\Chaos.NaCl\CryptoBytes.cs + + + Security\Cryptography\Chaos.NaCl\Ed25519.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array16.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Array8.cs + + + Security\Cryptography\Chaos.NaCl\Internal\ByteIntegerConverter.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\base2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\d2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cmov.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_cswap.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_invert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnegative.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_isnonzero.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_mul121666.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_neg.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_pow22523.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sq2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\fe_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\FieldElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_double_scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_frombytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_madd.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_msub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p1p1_to_p3.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p2_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_dbl.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_cached.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_p3_to_p2.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_precomp_0.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_scalarmult_base.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_sub.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\ge_tobytes.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\GroupElement.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\keypair.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\open.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\scalarmult.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_clamp.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_mul_add.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sc_reduce.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sign.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Ed25519Ref10\sqrtm1.cs + + + Security\Cryptography\Chaos.NaCl\Internal\InternalAssert.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Poly1305Donna.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\Salsa20.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Salsa\SalsaCore.cs + + + Security\Cryptography\Chaos.NaCl\Internal\Sha512Internal.cs + + + Security\Cryptography\Chaos.NaCl\MontgomeryCurve25519.cs + + + Security\Cryptography\Chaos.NaCl\Sha512.cs + Security\Cryptography\AsymmetricCipher.cs + + Security\Cryptography\Bcrypt.cs + Security\Cryptography\BlockCipher.cs @@ -919,6 +1105,12 @@ Security\Cryptography\DsaKey.cs + + Security\Cryptography\ED25519DigitalSignature.cs + + + Security\Cryptography\ED25519Key.cs + Security\Cryptography\HMACMD5.cs @@ -937,6 +1129,12 @@ Security\Cryptography\Key.cs + + Security\Cryptography\EcdsaDigitalSignature.cs + + + Security\Cryptography\EcdsaKey.cs + Security\Cryptography\RsaDigitalSignature.cs @@ -982,6 +1180,24 @@ Security\KeyExchangeDiffieHellmanGroupSha1.cs + + Security\KeyExchangeEC.cs + + + Security\KeyExchangeECCurve25519.cs + + + Security\KeyExchangeECDH.cs + + + Security\KeyExchangeECDH256.cs + + + Security\KeyExchangeECDH384.cs + + + Security\KeyExchangeECDH521.cs + Security\KeyHostAlgorithm.cs diff --git a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs index b0c40cb4c..5dd468b04 100644 --- a/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs +++ b/src/Renci.SshNet/Security/BouncyCastle/crypto/prng/CryptoApiRandomGenerator.cs @@ -1,5 +1,3 @@ -#if !(NETCF_1_0 || PORTABLE) - using System; using System.Security.Cryptography; @@ -69,5 +67,3 @@ public virtual void NextBytes(byte[] bytes, int start, int len) #endregion } } - -#endif diff --git a/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs b/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs index b618501a8..635a25409 100644 --- a/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs +++ b/src/Renci.SshNet/Security/BouncyCastle/math/BigInteger.cs @@ -10,7 +10,7 @@ namespace Renci.SshNet.Security.Org.BouncyCastle.Math { -#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE) +#if FEATURE_BINARY_SERIALIZATION [Serializable] #endif internal class BigInteger diff --git a/src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs b/src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs index d71893840..25a442e48 100644 --- a/src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs +++ b/src/Renci.SshNet/Security/BouncyCastle/security/SecurityUtilityException.cs @@ -2,7 +2,7 @@ namespace Renci.SshNet.Security.Org.BouncyCastle.Security { -#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT || PORTABLE) +#if FEATURE_BINARY_SERIALIZATION [Serializable] #endif internal class SecurityUtilityException From 982ef10ae74220ecc42a94b24d02628c16b3ee61 Mon Sep 17 00:00:00 2001 From: drieseng Date: Sat, 11 Jan 2020 17:05:32 +0100 Subject: [PATCH 8/8] Update readme for the new key exchange methods, host key algorithms and private key formats. --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0cc04fb9b..eaf0b3e53 100644 --- a/README.md +++ b/README.md @@ -18,22 +18,70 @@ This project was inspired by **Sharp.SSH** library which was ported from java an * Remote, dynamic and local port forwarding * Shell/Terminal implementation * Specify key file pass phrase -* Use multiple key files to authenticate -* Supports 3des-cbc, aes128-cbc, aes192-cbc, aes256-cbc, aes128-ctr, aes192-ctr, aes256-ctr, blowfish-cbc, cast128-cbc, arcfour and twofish encryptions +* Use multiple key files to authenticate * Supports publickey, password and keyboard-interactive authentication methods -* Supports RSA and DSA private key -* Supports DES-EDE3-CBC, DES-EDE3-CFB, DES-CBC, AES-128-CBC, AES-192-CBC and AES-256-CBC algorithms for private key encryption * Supports two-factor or higher authentication * Supports SOCKS4, SOCKS5 and HTTP Proxy +## Encryption Method + +**SSH.NET** supports the following encryption methods: +* aes256-ctr +* 3des-cbc +* aes128-cbc +* aes192-cbc +* aes256-cbc +* blowfish-cbc +* twofish-cbc +* twofish192-cbc +* twofish128-cbc +* twofish256-cbc +* arcfour +* arcfour128 +* arcfour256 +* cast128-cbc +* aes128-ctr +* aes192-ctr + ## Key Exchange Method **SSH.NET** supports the following key exchange methods: +* curve25519-sha256 +* curve25519-sha256@libssh.org +* ecdh-sha2-nistp256 +* ecdh-sha2-nistp384 +* ecdh-sha2-nistp521 * diffie-hellman-group-exchange-sha256 * diffie-hellman-group-exchange-sha1 * diffie-hellman-group14-sha1 * diffie-hellman-group1-sha1 +## Public Key Authenntication + +**SSH.NET** supports the following private key formats: +* RSA in OpenSSL PEM and ssh.com format +* DSA in OpenSSL PEM and ssh.com format +* ECDSA 256/384/521 in OpenSSL PEM format +* ED25519 in OpenSSH key format + +Private keys can be encrypted using one of the following cipher methods: +* DES-EDE3-CBC +* DES-EDE3-CFB +* DES-CBC +* AES-128-CBC +* AES-192-CBC +* AES-256-CBC + +## Host Key Algorithms + +**SSH.NET** supports the following host key algorithms: +* ssh-ed25519 +* ecdsa-sha2-nistp256 +* ecdsa-sha2-nistp384 +* ecdsa-sha2-nistp521 +* ssh-rsa +* ssh-dss + ## Message Authentication Code **SSH.NET** supports the following MAC algorithms: