Skip to content

Commit 629a3d1

Browse files
scott-xuRob-Hague
andcommitted
Manually pad input in CTR mode as well. Update AesCipherTest.
Co-Authored-By: Rob Hague <[email protected]>
1 parent 0f98ada commit 629a3d1

File tree

3 files changed

+776
-537
lines changed

3 files changed

+776
-537
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public override byte[] Encrypt(byte[] input, int offset, int length)
8787
}
8888
else if (length % _blockSize > 0)
8989
{
90-
if (_mode is CfbCipherMode or OfbCipherMode)
90+
if (_mode is CfbCipherMode or OfbCipherMode or CtrCipherMode)
9191
{
9292
paddingLength = _blockSize - (length % _blockSize);
9393
input = input.Take(offset, length);
@@ -143,7 +143,7 @@ public override byte[] Decrypt(byte[] input, int offset, int length)
143143
var paddingLength = 0;
144144
if (length % _blockSize > 0)
145145
{
146-
if (_padding is null && _mode is CfbCipherMode or OfbCipherMode)
146+
if (_padding is null && _mode is CfbCipherMode or OfbCipherMode or CtrCipherMode)
147147
{
148148
paddingLength = _blockSize - (length % _blockSize);
149149
input = input.Take(offset, length);

test/Renci.SshNet.Tests/Classes/Security/Cryptography/Ciphers/AesCipherTest.Gen.cs.txt

Lines changed: 103 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -21,109 +21,130 @@ foreach ((string mode, (string modeCode, CipherMode? bclMode)) in modes)
2121
{
2222
foreach (int keySize in new int[] { 128, 192, 256 })
2323
{
24-
foreach (int inputLength in new int[] { 16, 32, 64 })
24+
foreach (int inputLength in new int[] { 16, 35, 64 })
2525
{
26-
byte[] input = new byte[inputLength];
27-
random.NextBytes(input);
26+
foreach (bool pad in new bool[] { false, true })
27+
{
28+
// It is not allowed to use no padding on non-block lengths
29+
// It makes sense in cfb, ctr and ofb modes
30+
if (!pad && inputLength % 16 != 0 && mode is not "cfb" or "ctr" or "ofb")
31+
{
32+
continue;
33+
}
2834

29-
byte[] key = new byte[keySize / 8];
30-
random.NextBytes(key);
35+
// It does not make sense to test padding for stream cipher modes
36+
// (and the OpenSSL, BCL implementations differ)
37+
if (pad && mode is "cfb" or "ctr" or "ofb")
38+
{
39+
continue;
40+
}
3141

32-
byte[] iv = new byte[16];
33-
random.NextBytes(iv);
42+
byte[] input = new byte[inputLength];
43+
random.NextBytes(input);
3444

35-
StringBuilder openSslCmd = new();
45+
byte[] key = new byte[keySize / 8];
46+
random.NextBytes(key);
3647

37-
openSslCmd.Append($"echo -n -e '{string.Join("", input.Select(b => $"\\x{b:x2}"))}' |");
38-
openSslCmd.Append($" openssl enc -e -aes-{keySize}-{mode}");
39-
openSslCmd.Append($" -K {Convert.ToHexString(key)}");
40-
if (mode != "ecb")
41-
{
42-
openSslCmd.Append($" -iv {Convert.ToHexString(iv)}");
43-
}
44-
openSslCmd.Append(" -nopad");
48+
byte[] iv = new byte[16];
49+
random.NextBytes(iv);
4550

46-
ProcessStartInfo pi = new("wsl", openSslCmd.ToString())
47-
{
48-
RedirectStandardOutput = true,
49-
RedirectStandardError = true,
50-
};
51+
StringBuilder openSslCmd = new();
5152

52-
byte[] expected;
53-
string error;
53+
openSslCmd.Append($"echo -n -e '{string.Join("", input.Select(b => $"\\x{b:x2}"))}' |");
54+
openSslCmd.Append($" openssl enc -e -aes-{keySize}-{mode}");
55+
openSslCmd.Append($" -K {Convert.ToHexString(key)}");
56+
if (mode != "ecb")
57+
{
58+
openSslCmd.Append($" -iv {Convert.ToHexString(iv)}");
59+
}
5460

55-
using (MemoryStream ms = new())
56-
{
57-
var p = Process.Start(pi);
58-
p.StandardOutput.BaseStream.CopyTo(ms);
59-
error = p.StandardError.ReadToEnd();
61+
if (!pad)
62+
{
63+
openSslCmd.Append(" -nopad");
64+
}
6065

61-
p.WaitForExit();
66+
ProcessStartInfo pi = new("wsl", openSslCmd.ToString())
67+
{
68+
RedirectStandardOutput = true,
69+
RedirectStandardError = true,
70+
};
6271

63-
expected = ms.ToArray();
64-
}
72+
byte[] expected;
73+
string error;
6574

66-
tw.WriteLine("[TestMethod]");
67-
tw.WriteLine($"public void AES_{mode.ToUpper()}_{keySize}_Length{inputLength}()");
68-
tw.WriteLine("{");
69-
tw.Indent++;
75+
using (MemoryStream ms = new())
76+
{
77+
var p = Process.Start(pi);
78+
p.StandardOutput.BaseStream.CopyTo(ms);
79+
error = p.StandardError.ReadToEnd();
7080

71-
WriteBytes(input);
72-
WriteBytes(key);
73-
if (mode != "ecb")
74-
{
75-
WriteBytes(iv);
76-
}
77-
tw.WriteLine();
81+
p.WaitForExit();
7882

79-
if (!string.IsNullOrWhiteSpace(error))
80-
{
81-
tw.WriteLine($"// {openSslCmd}");
82-
tw.WriteLine($"Assert.Fail(@\"{error}\");");
83+
expected = ms.ToArray();
84+
}
8385

84-
tw.Indent--;
85-
tw.WriteLine("}");
86-
tw.WriteLine();
87-
continue;
88-
}
86+
tw.WriteLine("[TestMethod]");
87+
tw.WriteLine($"public void AES_{mode.ToUpper()}_{keySize}_Length{inputLength}_{(pad ? "Pad" : "NoPad")}()");
88+
tw.WriteLine("{");
89+
tw.Indent++;
8990

90-
tw.WriteLine($"// {openSslCmd} | hd"); // pipe to hexdump
91-
WriteBytes(expected);
92-
tw.WriteLine();
93-
tw.WriteLine($"var actual = new AesCipher(key, {modeCode}, pkcs7Padding: false).Encrypt(input);");
94-
tw.WriteLine();
95-
tw.WriteLine($"CollectionAssert.AreEqual(expected, actual);");
91+
WriteBytes(input);
92+
WriteBytes(key);
93+
if (mode != "ecb")
94+
{
95+
WriteBytes(iv);
96+
}
97+
tw.WriteLine();
9698

97-
if (bclMode is not null and not CipherMode.OFB)
98-
{
99-
// Verify the OpenSSL result is the same as the .NET BCL, just to be sure
100-
Aes bcl = Aes.Create();
101-
bcl.Key = key;
102-
bcl.IV = iv;
103-
bcl.Mode = bclMode.Value;
104-
bcl.Padding = PaddingMode.None;
105-
bcl.FeedbackSize = 128; // .NET is CFB8 by default, OpenSSL is CFB128
106-
byte[] bclBytes = bcl.CreateEncryptor().TransformFinalBlock(input, 0, input.Length);
107-
108-
if (!bclBytes.AsSpan().SequenceEqual(expected))
99+
if (!string.IsNullOrWhiteSpace(error))
109100
{
110-
tw.WriteLine();
111-
tw.WriteLine(@"Assert.Inconclusive(@""OpenSSL does not match the .NET BCL");
112-
tw.Indent++;
113-
tw.WriteLine($@"OpenSSL: {Convert.ToHexString(expected)}");
114-
tw.WriteLine($@"BCL: {Convert.ToHexString(bclBytes)}"");");
101+
tw.WriteLine($"// {openSslCmd}");
102+
tw.WriteLine($"Assert.Fail(@\"{error}\");");
103+
115104
tw.Indent--;
105+
tw.WriteLine("}");
106+
tw.WriteLine();
107+
continue;
116108
}
117-
}
118109

119-
tw.WriteLine();
120-
tw.WriteLine($"var decrypted = new AesCipher(key, {modeCode}, pkcs7Padding: false).Decrypt(actual);");
121-
tw.WriteLine();
122-
tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted);");
110+
tw.WriteLine($"// {openSslCmd} | hd"); // pipe to hexdump
111+
WriteBytes(expected);
112+
tw.WriteLine();
113+
tw.WriteLine($"var actual = new AesCipher(key, {modeCode}, pkcs7Padding: {(pad ? "true" : "false")}).Encrypt(input);");
114+
tw.WriteLine();
115+
tw.WriteLine($"CollectionAssert.AreEqual(expected, actual);");
116+
117+
if (bclMode is not null and not CipherMode.OFB and not CipherMode.CFB)
118+
{
119+
// Verify the OpenSSL result is the same as the .NET BCL, just to be sure
120+
Aes bcl = Aes.Create();
121+
bcl.Key = key;
122+
bcl.IV = iv;
123+
bcl.Mode = bclMode.Value;
124+
bcl.Padding = pad ? PaddingMode.PKCS7 : PaddingMode.None;
125+
bcl.FeedbackSize = 128; // .NET is CFB8 by default, OpenSSL is CFB128
126+
byte[] bclBytes = bcl.CreateEncryptor().TransformFinalBlock(input, 0, input.Length);
127+
128+
if (!bclBytes.AsSpan().SequenceEqual(expected))
129+
{
130+
tw.WriteLine();
131+
tw.WriteLine(@"Assert.Inconclusive(@""OpenSSL does not match the .NET BCL");
132+
tw.Indent++;
133+
tw.WriteLine($@"OpenSSL: {Convert.ToHexString(expected)}");
134+
tw.WriteLine($@"BCL: {Convert.ToHexString(bclBytes)}"");");
135+
tw.Indent--;
136+
}
137+
}
123138

124-
tw.Indent--;
125-
tw.WriteLine("}");
126-
tw.WriteLine();
139+
tw.WriteLine();
140+
tw.WriteLine($"var decrypted = new AesCipher(key, {modeCode}, pkcs7Padding: {(pad ? "true" : "false")}).Decrypt(actual);");
141+
tw.WriteLine();
142+
tw.WriteLine($"CollectionAssert.AreEqual(input, decrypted);");
143+
144+
tw.Indent--;
145+
tw.WriteLine("}");
146+
tw.WriteLine();
147+
}
127148
}
128149
}
129150
}

0 commit comments

Comments
 (0)