Skip to content

Commit 0303aa9

Browse files
committed
Decrypt should take into account *manual* padding for length of inputBuffer passed to DecryptBlock and unpadding for the final output if padding is not specified and mode is CFB or OFB.
1 parent 84c3a8d commit 0303aa9

File tree

4 files changed

+66
-10
lines changed

4 files changed

+66
-10
lines changed

src/Renci.SshNet/PrivateKeyFile.PKCS1.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public Key Parse()
5656
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));
5757
break;
5858
case "DES-EDE3-CFB":
59-
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), new PKCS7Padding()));
59+
cipher = new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CfbCipherMode(iv), padding: null));
6060
break;
6161
case "DES-CBC":
6262
cipher = new CipherInfo(64, (key, iv) => new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding()));

src/Renci.SshNet/PrivateKeyFile.SSHCOM.cs

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

1312
namespace Renci.SshNet
1413
{
@@ -52,7 +51,7 @@ public Key Parse()
5251
}
5352

5453
var key = GetCipherKey(_passPhrase, 192 / 8);
55-
var ssh2Сipher = new TripleDesCipher(key, new CbcCipherMode(new byte[8]), new PKCS7Padding());
54+
var ssh2Сipher = new TripleDesCipher(key, new CbcCipherMode(new byte[8]), padding: null);
5655
keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
5756
}
5857
else

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
using Org.BouncyCastle.Crypto.Paddings;
44

5+
using Renci.SshNet.Common;
56
using Renci.SshNet.Security.Cryptography.Ciphers;
7+
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
68
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;
79

810
namespace Renci.SshNet.Security.Cryptography
@@ -87,7 +89,18 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
8789
}
8890
else if (length % _blockSize > 0)
8991
{
90-
throw new ArgumentException(string.Format("The data block size is incorrect for {0}.", GetType().Name), "data");
92+
if (_mode is CfbCipherMode or OfbCipherMode)
93+
{
94+
var paddingLength = _blockSize - (length % _blockSize);
95+
input = input.Take(offset, length);
96+
Array.Resize(ref input, length + paddingLength);
97+
length += paddingLength;
98+
offset = 0;
99+
}
100+
else
101+
{
102+
throw new ArgumentException(string.Format("The data block size is incorrect for {0}.", GetType().Name), "data");
103+
}
91104
}
92105

93106
var output = new byte[length];
@@ -124,16 +137,21 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
124137
/// </returns>
125138
public override byte[] Decrypt(byte[] input, int offset, int length)
126139
{
140+
var paddingLength = 0;
127141
if (length % _blockSize > 0)
128142
{
129-
if (_padding is null)
143+
if (_padding is null && _mode is CfbCipherMode or OfbCipherMode)
144+
{
145+
paddingLength = _blockSize - (length % _blockSize);
146+
input = input.Take(offset, length);
147+
length += paddingLength;
148+
Array.Resize(ref input, length);
149+
offset = 0;
150+
}
151+
else
130152
{
131153
throw new ArgumentException(string.Format("The data block size is incorrect for {0}.", GetType().Name), "data");
132154
}
133-
134-
input = _padding.Pad(_blockSize, input, offset, length);
135-
offset = 0;
136-
length = input.Length;
137155
}
138156

139157
var output = new byte[length];
@@ -158,7 +176,11 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
158176

159177
if (_padding is PKCS7Padding)
160178
{
161-
var paddingLength = new Pkcs7Padding().PadCount(output);
179+
paddingLength = new Pkcs7Padding().PadCount(output);
180+
}
181+
182+
if (paddingLength > 0)
183+
{
162184
Array.Resize(ref output, output.Length - paddingLength);
163185
}
164186

test/Renci.SshNet.Tests/Classes/Security/Cryptography/BlockCipherTest.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using Renci.SshNet.Security.Cryptography;
77
using Renci.SshNet.Security.Cryptography.Ciphers;
8+
using Renci.SshNet.Security.Cryptography.Ciphers.Modes;
89
using Renci.SshNet.Security.Cryptography.Ciphers.Paddings;
910
using Renci.SshNet.Tests.Common;
1011

@@ -78,6 +79,27 @@ public void DecryptShouldTakeIntoAccountUnPaddingForTheFinalOutput()
7879
Assert.IsTrue(output.SequenceEqual(actual));
7980
}
8081

82+
[TestMethod]
83+
public void DecryptShouldTakeIntoAccountManualPaddingForLengthOfInputBufferPassedToDecryptBlockAndUnPaddingForTheFinalOutput_CFB()
84+
{
85+
var input = new byte[] { 0x0a, 0x00, 0x03, 0x02, 0x06 };
86+
var output = new byte[] { 0x2c, 0x1a, 0x05, 0x00, 0x68 };
87+
var key = new byte[] { 0x17, 0x78, 0x56, 0xe1, 0x3e, 0xbd, 0x3e, 0x50, 0x1d, 0x79, 0x3f, 0x0f, 0x55, 0x37, 0x45, 0x54 };
88+
var blockCipher = new BlockCipherStub(key, 8, new CfbCipherModeStub(new byte[8]), null)
89+
{
90+
DecryptBlockDelegate = (inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset) =>
91+
{
92+
Assert.AreEqual(8, inputBuffer.Length);
93+
Buffer.BlockCopy(output, 0, outputBuffer, 0, output.Length);
94+
return inputBuffer.Length;
95+
}
96+
};
97+
98+
var actual = blockCipher.Decrypt(input);
99+
100+
Assert.IsTrue(output.SequenceEqual(actual));
101+
}
102+
81103

82104
private class BlockCipherStub : BlockCipher
83105
{
@@ -98,5 +120,18 @@ public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputC
98120
return DecryptBlockDelegate(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
99121
}
100122
}
123+
124+
private class CfbCipherModeStub : CfbCipherMode
125+
{
126+
public CfbCipherModeStub(byte[] iv)
127+
: base(iv)
128+
{
129+
}
130+
131+
public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
132+
{
133+
return Cipher.DecryptBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset);
134+
}
135+
}
101136
}
102137
}

0 commit comments

Comments
 (0)