Skip to content

Commit d06dfa5

Browse files
authored
Use System.Numerics.BigInteger (#1469)
Instead of the point-in-time copy
1 parent fe827a5 commit d06dfa5

30 files changed

+221
-6915
lines changed

src/Renci.SshNet/Common/BigInteger.cs

-4,956
This file was deleted.

src/Renci.SshNet/Common/DerData.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Buffers.Binary;
33
using System.Collections.Generic;
4+
using System.Numerics;
45

56
namespace Renci.SshNet.Common
67
{
@@ -96,7 +97,11 @@ public BigInteger ReadBigInteger()
9697

9798
var data = ReadBytes(length);
9899

100+
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
101+
return new BigInteger(data, isBigEndian: true);
102+
#else
99103
return new BigInteger(data.Reverse());
104+
#endif
100105
}
101106

102107
/// <summary>
@@ -213,7 +218,7 @@ public void Write(uint data)
213218
/// <param name="data">BigInteger data to write.</param>
214219
public void Write(BigInteger data)
215220
{
216-
var bytes = data.ToByteArray().Reverse();
221+
var bytes = data.ToByteArray(isBigEndian: true);
217222
_data.Add(Integer);
218223
var length = GetLength(bytes.Length);
219224
WriteBytes(length);

src/Renci.SshNet/Common/Extensions.cs

+21-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Globalization;
55
using System.Net;
66
using System.Net.Sockets;
7+
using System.Numerics;
78
using System.Text;
89

910
using Renci.SshNet.Abstractions;
@@ -14,7 +15,7 @@ namespace Renci.SshNet.Common
1415
/// <summary>
1516
/// Collection of different extension methods.
1617
/// </summary>
17-
internal static partial class Extensions
18+
internal static class Extensions
1819
{
1920
internal static byte[] ToArray(this ServiceName serviceName)
2021
{
@@ -45,26 +46,35 @@ internal static ServiceName ToServiceName(this byte[] data)
4546

4647
internal static BigInteger ToBigInteger(this byte[] data)
4748
{
49+
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
50+
return new BigInteger(data, isBigEndian: true);
51+
#else
4852
var reversed = new byte[data.Length];
4953
Buffer.BlockCopy(data, 0, reversed, 0, data.Length);
5054
return new BigInteger(reversed.Reverse());
55+
#endif
5156
}
5257

5358
/// <summary>
5459
/// Initializes a new instance of the <see cref="BigInteger"/> structure using the SSH BigNum2 Format.
5560
/// </summary>
5661
public static BigInteger ToBigInteger2(this byte[] data)
5762
{
63+
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
64+
return new BigInteger(data, isBigEndian: true, isUnsigned: true);
65+
#else
5866
if ((data[0] & (1 << 7)) != 0)
5967
{
6068
var buf = new byte[data.Length + 1];
6169
Buffer.BlockCopy(data, 0, buf, 1, data.Length);
62-
data = buf;
70+
return new BigInteger(buf.Reverse());
6371
}
6472

6573
return data.ToBigInteger();
74+
#endif
6675
}
6776

77+
#if NETFRAMEWORK || NETSTANDARD2_0
6878
public static byte[] ToByteArray(this BigInteger bigInt, bool isUnsigned = false, bool isBigEndian = false)
6979
{
7080
var data = bigInt.ToByteArray();
@@ -81,6 +91,15 @@ public static byte[] ToByteArray(this BigInteger bigInt, bool isUnsigned = false
8191

8292
return data;
8393
}
94+
#endif
95+
96+
#if !NET6_0_OR_GREATER
97+
public static long GetBitLength(this BigInteger bigint)
98+
{
99+
// Taken from https://github.com/dotnet/runtime/issues/31308
100+
return (long)Math.Ceiling(BigInteger.Log(bigint.Sign < 0 ? -bigint : bigint + 1, 2));
101+
}
102+
#endif
84103

85104
// See https://github.com/dotnet/runtime/blob/9b57a265c7efd3732b035bade005561a04767128/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs#L51
86105
public static byte[] ExportKeyParameter(this BigInteger value, int length)

src/Renci.SshNet/Common/SshData.cs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Numerics;
34
using System.Text;
45

56
namespace Renci.SshNet.Common

src/Renci.SshNet/Common/SshDataStream.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Globalization;
33
using System.IO;
4+
using System.Numerics;
45
using System.Text;
56

67
namespace Renci.SshNet.Common
@@ -110,7 +111,8 @@ public void Write(ulong value)
110111
/// <param name="data">The <see cref="BigInteger" /> to write.</param>
111112
public void Write(BigInteger data)
112113
{
113-
var bytes = data.ToByteArray().Reverse();
114+
var bytes = data.ToByteArray(isBigEndian: true);
115+
114116
WriteBinary(bytes, 0, bytes.Length);
115117
}
116118

@@ -211,9 +213,13 @@ public void WriteBinary(byte[] buffer, int offset, int count)
211213
/// </returns>
212214
public BigInteger ReadBigInt()
213215
{
214-
var length = ReadUInt32();
215-
var data = ReadBytes((int)length);
216+
var data = ReadBinary();
217+
218+
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
219+
return new BigInteger(data, isBigEndian: true);
220+
#else
216221
return new BigInteger(data.Reverse());
222+
#endif
217223
}
218224

219225
/// <summary>

src/Renci.SshNet/Messages/Transport/KeyExchangeDhGroupExchangeGroup.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Renci.SshNet.Common;
1+
using System.Numerics;
2+
3+
using Renci.SshNet.Common;
24

35
namespace Renci.SshNet.Messages.Transport
46
{

src/Renci.SshNet/Messages/Transport/KeyExchangeEcdhInitMessage.cs

-17
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
using System;
22

3-
using Renci.SshNet.Common;
4-
53
namespace Renci.SshNet.Messages.Transport
64
{
75
/// <summary>
@@ -57,21 +55,6 @@ public KeyExchangeEcdhInitMessage(byte[] q)
5755
QC = q;
5856
}
5957

60-
/// <summary>
61-
/// Initializes a new instance of the <see cref="KeyExchangeEcdhInitMessage"/> class.
62-
/// </summary>
63-
public KeyExchangeEcdhInitMessage(BigInteger d, BigInteger q)
64-
{
65-
var dBytes = d.ToByteArray().Reverse();
66-
var qBytes = q.ToByteArray().Reverse();
67-
68-
var data = new byte[dBytes.Length + qBytes.Length + 1];
69-
data[0] = 0x04;
70-
Buffer.BlockCopy(dBytes, 0, data, 1, dBytes.Length);
71-
Buffer.BlockCopy(qBytes, 0, data, dBytes.Length + 1, qBytes.Length);
72-
QC = data;
73-
}
74-
7558
/// <summary>
7659
/// Called when type specific data need to be loaded.
7760
/// </summary>

src/Renci.SshNet/PrivateKeyFile.cs

+4-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Diagnostics;
44
using System.Globalization;
55
using System.IO;
6+
using System.Numerics;
67
using System.Security.Cryptography;
78
using System.Text;
89
using System.Text.RegularExpressions;
@@ -713,22 +714,17 @@ public BigInteger ReadBigIntWithBits()
713714

714715
length = (length + 7) / 8;
715716

716-
var data = base.ReadBytes(length);
717-
var bytesArray = new byte[data.Length + 1];
718-
Buffer.BlockCopy(data, 0, bytesArray, 1, data.Length);
719-
720-
return new BigInteger(bytesArray.Reverse());
717+
return base.ReadBytes(length).ToBigInteger2();
721718
}
722719

723720
public BigInteger ReadBignum()
724721
{
725-
return new BigInteger(ReadBignum2().Reverse());
722+
return DataStream.ReadBigInt();
726723
}
727724

728725
public byte[] ReadBignum2()
729726
{
730-
var length = (int)base.ReadUInt32();
731-
return base.ReadBytes(length);
727+
return ReadBinary();
732728
}
733729

734730
protected override void LoadData()

src/Renci.SshNet/Security/Cryptography/DsaKey.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System;
3+
using System.Numerics;
34
using System.Security.Cryptography;
45

56
using Renci.SshNet.Common;
@@ -46,7 +47,7 @@ public override int KeyLength
4647
{
4748
get
4849
{
49-
return P.BitLength;
50+
return (int)P.GetBitLength();
5051
}
5152
}
5253

src/Renci.SshNet/Security/Cryptography/ED25519Key.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Numerics;
23

34
using Org.BouncyCastle.Math.EC.Rfc8032;
45

@@ -87,7 +88,8 @@ public ED25519Key(SshKeyData publicKeyData)
8788
throw new ArgumentException($"Invalid Ed25519 public key data ({publicKeyData.Name}, {publicKeyData.Keys.Length}).", nameof(publicKeyData));
8889
}
8990

90-
PublicKey = publicKeyData.Keys[0].ToByteArray().Reverse().TrimLeadingZeros().Pad(Ed25519.PublicKeySize);
91+
PublicKey = publicKeyData.Keys[0].ToByteArray(isBigEndian: true).TrimLeadingZeros().Pad(Ed25519.PublicKeySize);
92+
PrivateKey = new byte[Ed25519.SecretKeySize];
9193
}
9294

9395
/// <summary>

src/Renci.SshNet/Security/Cryptography/EcdsaDigitalSignature.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,11 @@ public byte[] Signature
9595
{
9696
var signed_r = new byte[_signature_size / 2];
9797
Buffer.BlockCopy(value, 0, signed_r, 0, signed_r.Length);
98-
_signature_r = signed_r.ToBigInteger2().ToByteArray().Reverse();
98+
_signature_r = signed_r.ToBigInteger2().ToByteArray(isBigEndian: true);
9999

100100
var signed_s = new byte[_signature_size / 2];
101101
Buffer.BlockCopy(value, signed_r.Length, signed_s, 0, signed_s.Length);
102-
_signature_s = signed_s.ToBigInteger2().ToByteArray().Reverse();
102+
_signature_s = signed_s.ToBigInteger2().ToByteArray(isBigEndian: true);
103103
}
104104
}
105105

@@ -122,8 +122,8 @@ protected override void LoadData()
122122

123123
protected override void SaveData()
124124
{
125-
WriteBinaryString(_signature_r.ToBigInteger2().ToByteArray().Reverse());
126-
WriteBinaryString(_signature_s.ToBigInteger2().ToByteArray().Reverse());
125+
WriteBinaryString(_signature_r.ToBigInteger2().ToByteArray(isBigEndian: true));
126+
WriteBinaryString(_signature_s.ToBigInteger2().ToByteArray(isBigEndian: true));
127127
}
128128

129129
protected override int BufferCapacity

src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
#nullable enable
1+
#nullable enable
22
using System;
33
using System.Diagnostics;
4+
using System.Numerics;
45
using System.Security.Cryptography;
56
using System.Text;
67

@@ -145,7 +146,11 @@ public override BigInteger[] Public
145146
Buffer.BlockCopy(qy, 0, q, qx.Length + 1, qy.Length);
146147

147148
// returns Curve-Name and x/y as ECPoint
149+
#if NETSTANDARD2_1_OR_GREATER || NET6_0_OR_GREATER
150+
return new[] { curve, new BigInteger(q, isBigEndian: true) };
151+
#else
148152
return new[] { curve, new BigInteger(q.Reverse()) };
153+
#endif
149154
}
150155
}
151156

@@ -191,10 +196,10 @@ public EcdsaKey(SshKeyData publicKeyData)
191196
throw new ArgumentException($"Invalid ECDSA public key data. ({publicKeyData.Name}, {publicKeyData.Keys.Length}).", nameof(publicKeyData));
192197
}
193198

194-
var curve_s = Encoding.ASCII.GetString(publicKeyData.Keys[0].ToByteArray().Reverse());
199+
var curve_s = Encoding.ASCII.GetString(publicKeyData.Keys[0].ToByteArray(isBigEndian: true));
195200
var curve_oid = GetCurveOid(curve_s);
196201

197-
var publickey = publicKeyData.Keys[1].ToByteArray().Reverse();
202+
var publickey = publicKeyData.Keys[1].ToByteArray(isBigEndian: true);
198203
_impl = Import(curve_oid, publickey, privatekey: null);
199204
}
200205

src/Renci.SshNet/Security/Cryptography/Key.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Renci.SshNet.Common;
1+
using System.Numerics;
2+
23
using Renci.SshNet.Security.Cryptography;
34

45
namespace Renci.SshNet.Security

src/Renci.SshNet/Security/Cryptography/RsaKey.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System;
3+
using System.Numerics;
34
using System.Security.Cryptography;
45

56
using Renci.SshNet.Common;
@@ -96,7 +97,7 @@ public override int KeyLength
9697
{
9798
get
9899
{
99-
return Modulus.BitLength;
100+
return (int)Modulus.GetBitLength();
100101
}
101102
}
102103

src/Renci.SshNet/Security/GroupExchangeHashData.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Numerics;
23

34
using Renci.SshNet.Common;
45

@@ -38,13 +39,13 @@ public string ClientVersion
3839
public BigInteger Prime
3940
{
4041
private get { return _prime.ToBigInteger(); }
41-
set { _prime = value.ToByteArray().Reverse(); }
42+
set { _prime = value.ToByteArray(isBigEndian: true); }
4243
}
4344

4445
public BigInteger SubGroup
4546
{
4647
private get { return _subGroup.ToBigInteger(); }
47-
set { _subGroup = value.ToByteArray().Reverse(); }
48+
set { _subGroup = value.ToByteArray(isBigEndian: true); }
4849
}
4950

5051
public byte[] ClientExchangeValue { get; set; }

src/Renci.SshNet/Security/KeyExchangeDiffieHellman.cs

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Numerics;
23

4+
using Renci.SshNet.Abstractions;
35
using Renci.SshNet.Common;
46
using Renci.SshNet.Messages.Transport;
57

@@ -109,14 +111,26 @@ protected void PopulateClientExchangeValue()
109111
do
110112
{
111113
// Create private component
112-
_privateExponent = BigInteger.Random(privateExponentSize);
114+
_privateExponent = RandomBigInt(privateExponentSize);
113115

114116
// Generate public component
115117
clientExchangeValue = BigInteger.ModPow(_group, _privateExponent, _prime);
116118
}
117119
while (clientExchangeValue < 1 || clientExchangeValue > (_prime - 1));
118120

119-
_clientExchangeValue = clientExchangeValue.ToByteArray().Reverse();
121+
_clientExchangeValue = clientExchangeValue.ToByteArray(isBigEndian: true);
122+
}
123+
124+
/// <summary>
125+
/// Generates a new, random <see cref="BigInteger"/> of the specified length.
126+
/// </summary>
127+
/// <param name="bitLength">The number of bits for the new number.</param>
128+
/// <returns>A random number of the specified length.</returns>
129+
private static BigInteger RandomBigInt(int bitLength)
130+
{
131+
var bytesArray = CryptoAbstraction.GenerateRandom((bitLength / 8) + (((bitLength % 8) > 0) ? 1 : 0));
132+
bytesArray[bytesArray.Length - 1] = (byte)(bytesArray[bytesArray.Length - 1] & 0x7F); // Ensure not a negative value
133+
return new BigInteger(bytesArray);
120134
}
121135

122136
/// <summary>
@@ -129,7 +143,7 @@ protected virtual void HandleServerDhReply(byte[] hostKey, byte[] serverExchange
129143
{
130144
_serverExchangeValue = serverExchangeValue;
131145
_hostKey = hostKey;
132-
SharedKey = BigInteger.ModPow(serverExchangeValue.ToBigInteger(), _privateExponent, _prime).ToByteArray().Reverse();
146+
SharedKey = BigInteger.ModPow(serverExchangeValue.ToBigInteger(), _privateExponent, _prime).ToByteArray(isBigEndian: true);
133147
_signature = signature;
134148
}
135149
}

0 commit comments

Comments
 (0)