Skip to content

Commit a7b10e5

Browse files
committed
Use System.Security.Cryptography in DesCipher and TripleDesCipher; Fall back to use BouncyCastle if BCL doesn't support
1 parent 29997ae commit a7b10e5

24 files changed

+1425
-917
lines changed

src/Renci.SshNet/ConnectionInfo.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
using Renci.SshNet.Security;
1313
using Renci.SshNet.Security.Cryptography;
1414
using Renci.SshNet.Security.Cryptography.Ciphers;
15-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
1615

1716
namespace Renci.SshNet
1817
{
@@ -363,16 +362,16 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy
363362

364363
Encryptions = new Dictionary<string, CipherInfo>
365364
{
366-
{ "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
367-
{ "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
368-
{ "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false)) },
365+
{ "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
366+
{ "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
367+
{ "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false)) },
369368
{ "[email protected]", new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
370369
{ "[email protected]", new CipherInfo(256, (key, iv) => new AesGcmCipher(key, iv, aadLength: 4), isAead: true) },
371370
{ "[email protected]", new CipherInfo(512, (key, iv) => new ChaCha20Poly1305Cipher(key, aadLength: 4), isAead: true) },
372-
{ "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
373-
{ "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
374-
{ "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false)) },
375-
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) },
371+
{ "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
372+
{ "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
373+
{ "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
374+
{ "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false)) },
376375
};
377376

378377
HmacAlgorithms = new Dictionary<string, HashInfo>

src/Renci.SshNet/PrivateKeyFile.OpenSSH.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
using Renci.SshNet.Security;
99
using Renci.SshNet.Security.Cryptography;
1010
using Renci.SshNet.Security.Cryptography.Ciphers;
11-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
1211

1312
namespace Renci.SshNet
1413
{
@@ -91,25 +90,25 @@ public Key Parse()
9190
{
9291
case "3des-cbc":
9392
ivLength = 8;
94-
cipherInfo = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null));
93+
cipherInfo = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
9594
break;
9695
case "aes128-cbc":
97-
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
96+
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
9897
break;
9998
case "aes192-cbc":
100-
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
99+
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
101100
break;
102101
case "aes256-cbc":
103-
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: false));
102+
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: false));
104103
break;
105104
case "aes128-ctr":
106-
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
105+
cipherInfo = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
107106
break;
108107
case "aes192-ctr":
109-
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
108+
cipherInfo = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
110109
break;
111110
case "aes256-ctr":
112-
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CTR, pkcs7Padding: false));
111+
cipherInfo = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CTR, pkcs7Padding: false));
113112
break;
114113
115114
cipherInfo = new CipherInfo(128, (key, iv) => new AesGcmCipher(key, iv, aadLength: 0), isAead: true);

src/Renci.SshNet/PrivateKeyFile.PKCS1.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
using Renci.SshNet.Common;
1010
using Renci.SshNet.Security;
1111
using Renci.SshNet.Security.Cryptography.Ciphers;
12-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
13-
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;
1412

1513
namespace Renci.SshNet
1614
{
@@ -51,22 +49,22 @@ public Key Parse()
5149
switch (_cipherName)
5250
{
5351
case "DES-EDE3-CBC":
54-
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
52+
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
5553
break;
5654
case "DES-EDE3-CFB":
57-
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), padding: null));
55+
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, iv, BlockCipherMode.CFB, pkcs7Padding: false));
5856
break;
5957
case "DES-CBC":
60-
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
58+
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
6159
break;
6260
case "AES-128-CBC":
63-
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
61+
cipher = new CipherInfo(128, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
6462
break;
6563
case "AES-192-CBC":
66-
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
64+
cipher = new CipherInfo(192, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
6765
break;
6866
case "AES-256-CBC":
69-
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, AesCipherMode.CBC, pkcs7Padding: true));
67+
cipher = new CipherInfo(256, (key, iv) => new AesCipher(key, iv, BlockCipherMode.CBC, pkcs7Padding: true));
7068
break;
7169
default:
7270
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key cipher \"{0}\" is not supported.", _cipherName));

src/Renci.SshNet/PrivateKeyFile.SSHCOM.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Renci.SshNet.Common;
88
using Renci.SshNet.Security;
99
using Renci.SshNet.Security.Cryptography.Ciphers;
10-
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
1110

1211
namespace Renci.SshNet
1312
{
@@ -51,7 +50,7 @@ public Key Parse()
5150
}
5251

5352
var key = GetCipherKey(_passPhrase, 192 / 8);
54-
var ssh2Сipher = new TripleDesCipher(key, new CbcCipherMode(new byte[8]), padding: null);
53+
var ssh2Сipher = new TripleDesCipher(key, new byte[8], BlockCipherMode.CBC, pkcs7Padding: false);
5554
keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
5655
}
5756
else

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BclImpl.cs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
5252
{
5353
if (_aes.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB)
5454
{
55+
// Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block.
56+
// See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21
5557
paddingLength = BlockSize - (length % BlockSize);
5658
input = input.Take(offset, length);
5759
length += paddingLength;
@@ -69,6 +71,7 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
6971

7072
if (paddingLength > 0)
7173
{
74+
// Manually unpad the output.
7275
Array.Resize(ref output, output.Length - paddingLength);
7376
}
7477

@@ -89,11 +92,12 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
8992
{
9093
if (_aes.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB)
9194
{
95+
// Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block.
96+
// See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21
9297
paddingLength = BlockSize - (length % BlockSize);
93-
var newInput = new byte[input.Length + paddingLength];
94-
Buffer.BlockCopy(input, offset, newInput, 0, length);
95-
input = newInput;
96-
length = input.Length;
98+
input = input.Take(offset, length);
99+
length += paddingLength;
100+
Array.Resize(ref input, length);
97101
offset = 0;
98102
}
99103
}
@@ -107,6 +111,7 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
107111

108112
if (paddingLength > 0)
109113
{
114+
// Manually unpad the output.
110115
Array.Resize(ref output, output.Length - paddingLength);
111116
}
112117

@@ -123,21 +128,11 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
123128
throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}.");
124129
}
125130

126-
private void Dispose(bool disposing)
127-
{
128-
if (disposing)
129-
{
130-
_aes.Dispose();
131-
_encryptor.Dispose();
132-
_decryptor.Dispose();
133-
}
134-
}
135-
136131
public void Dispose()
137132
{
138-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
139-
Dispose(disposing: true);
140-
GC.SuppressFinalize(this);
133+
_aes.Dispose();
134+
_encryptor.Dispose();
135+
_decryptor.Dispose();
141136
}
142137
}
143138
}

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.BlockImpl.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,11 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
3333
return _decryptor.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
3434
}
3535

36-
private void Dispose(bool disposing)
37-
{
38-
if (disposing)
39-
{
40-
_aes.Dispose();
41-
_encryptor.Dispose();
42-
_decryptor.Dispose();
43-
}
44-
}
45-
4636
public void Dispose()
4737
{
48-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
49-
Dispose(disposing: true);
50-
GC.SuppressFinalize(this);
38+
_aes.Dispose();
39+
_encryptor.Dispose();
40+
_decryptor.Dispose();
5141
}
5242
}
5343
}

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.CtrImpl.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,20 +105,10 @@ private static void ArrayXOR(byte[] buffer, byte[] data, int offset, int length)
105105
}
106106
}
107107

108-
private void Dispose(bool disposing)
109-
{
110-
if (disposing)
111-
{
112-
_aes.Dispose();
113-
_encryptor.Dispose();
114-
}
115-
}
116-
117108
public void Dispose()
118109
{
119-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
120-
Dispose(disposing: true);
121-
GC.SuppressFinalize(this);
110+
_aes.Dispose();
111+
_encryptor.Dispose();
122112
}
123113
}
124114
}

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipher.cs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,27 @@ public sealed partial class AesCipher : BlockCipher, IDisposable
1717
/// Initializes a new instance of the <see cref="AesCipher"/> class.
1818
/// </summary>
1919
/// <param name="key">The key.</param>
20-
/// <param name="mode">The mode.</param>
2120
/// <param name="iv">The IV.</param>
21+
/// <param name="mode">The mode.</param>
2222
/// <param name="pkcs7Padding">Enable PKCS7 padding.</param>
2323
/// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
2424
/// <exception cref="ArgumentException">Keysize is not valid for this algorithm.</exception>
25-
public AesCipher(byte[] key, byte[] iv, AesCipherMode mode, bool pkcs7Padding = false)
25+
public AesCipher(byte[] key, byte[] iv, BlockCipherMode mode, bool pkcs7Padding = false)
2626
: base(key, 16, mode: null, padding: null)
2727
{
28-
if (mode == AesCipherMode.OFB)
28+
if (mode == BlockCipherMode.OFB)
2929
{
3030
// OFB is not supported on modern .NET
3131
_impl = new BlockImpl(key, new OfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
3232
}
3333
#if !NET6_0_OR_GREATER
34-
else if (mode == AesCipherMode.CFB)
34+
else if (mode == BlockCipherMode.CFB)
3535
{
3636
// CFB not supported on NetStandard 2.1
3737
_impl = new BlockImpl(key, new CfbCipherMode(iv), pkcs7Padding ? new PKCS7Padding() : null);
3838
}
3939
#endif
40-
else if (mode == AesCipherMode.CTR)
40+
else if (mode == BlockCipherMode.CTR)
4141
{
4242
// CTR not supported by the BCL, use an optimized implementation
4343
_impl = new CtrImpl(key, iv);
@@ -76,24 +76,13 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
7676
return _impl.Decrypt(input, offset, length);
7777
}
7878

79-
/// <summary>
80-
/// Dispose the instance.
81-
/// </summary>
82-
/// <param name="disposing">Set to True to dispose of resouces.</param>
83-
public void Dispose(bool disposing)
79+
/// <inheritdoc/>
80+
public void Dispose()
8481
{
85-
if (disposing && _impl is IDisposable disposableImpl)
82+
if (_impl is IDisposable disposableImpl)
8683
{
8784
disposableImpl.Dispose();
8885
}
8986
}
90-
91-
/// <inheritdoc/>
92-
public void Dispose()
93-
{
94-
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
95-
Dispose(disposing: true);
96-
GC.SuppressFinalize(this);
97-
}
9887
}
9988
}

src/Renci.SshNet/Security/Cryptography/Ciphers/AesCipherMode.cs renamed to src/Renci.SshNet/Security/Cryptography/Ciphers/BlockCipherMode.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
namespace Renci.SshNet.Security.Cryptography.Ciphers
22
{
33
/// <summary>
4-
/// Custom AES Cipher Mode, follows System.Security.Cryptography.CipherMode.
4+
/// Custom Cipher Mode, follows System.Security.Cryptography.CipherMode.
55
/// </summary>
6-
public enum AesCipherMode
6+
public enum BlockCipherMode
77
{
88
/// <summary>Cipher Block Chain Mode.</summary>
99
CBC = 1,

0 commit comments

Comments
 (0)