Skip to content

Commit ceea8cd

Browse files
committed
Remove padding in decrypted string by default, add option not to; refactoring
1 parent 99791b9 commit ceea8cd

File tree

4 files changed

+120
-41
lines changed

4 files changed

+120
-41
lines changed

PaddingOracleDecryptor.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ class PaddingOracleDecryptor
1111
public PaddingOracleDecryptor(RemoteServerMock oracle)
1212
{
1313
this.oracle = oracle;
14-
paddingValueProvider = PaddingValueProviders.GetFromMode(oracle.Padding);
14+
paddingValueProvider = PaddingUtils.GetPaddingValueProviderFromMode(oracle.Padding);
1515
}
1616

17-
public string DecryptBlock(byte[] block, byte[] previousBlock)
17+
public byte[] DecryptBlock(byte[] block, byte[] previousBlock)
1818
{
1919
byte[] decrypted = new byte[block.Length];
2020
byte[] manipulatedPrevious = new byte[16];
@@ -48,7 +48,7 @@ public string DecryptBlock(byte[] block, byte[] previousBlock)
4848
}
4949
}
5050

51-
return Encoding.UTF8.GetString(decrypted, 0, decrypted.Length);
51+
return decrypted;
5252
}
5353

5454
}

PaddingUtils.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
4+
namespace Padding_Oracle_Attack
5+
{
6+
public delegate byte PaddingValueProvider(int pos, int paddingLength, int blockLength);
7+
public delegate byte[] PaddingRemover(byte[] block);
8+
9+
class PaddingUtils
10+
{
11+
public static PaddingValueProvider GetPaddingValueProviderFromMode(PaddingMode paddingMode)
12+
{
13+
switch (paddingMode)
14+
{
15+
case PaddingMode.PKCS7:
16+
return PKCS7_ValueProvider;
17+
18+
case PaddingMode.ANSIX923:
19+
return ANSIX923_ValueProvider;
20+
}
21+
throw new NotImplementedException(Enum.GetName(typeof(PaddingMode), paddingMode) + " is not supported");
22+
}
23+
24+
public static PaddingRemover GetPaddingRemoverFromMode(PaddingMode paddingMode)
25+
{
26+
switch (paddingMode)
27+
{
28+
case PaddingMode.PKCS7:
29+
return PKCS7_Remover;
30+
31+
case PaddingMode.ANSIX923:
32+
return ANSIX923_Remover;
33+
}
34+
throw new NotImplementedException(Enum.GetName(typeof(PaddingMode), paddingMode) + " is not supported");
35+
}
36+
37+
public static byte PKCS7_ValueProvider(int pos, int paddingLength, int blockLength)
38+
{
39+
return (byte)paddingLength;
40+
}
41+
42+
public static byte[] PKCS7_Remover(byte[] block)
43+
{
44+
var paddingLength = block[block.Length - 1];
45+
46+
if (paddingLength <= 1 && paddingLength >= block.Length)
47+
{
48+
throw new Exception("Incorrect padding");
49+
}
50+
51+
var contentLength = block.Length - paddingLength;
52+
53+
for (int i = block.Length - 2; i >= contentLength; --i)
54+
{
55+
if (block[i] != paddingLength)
56+
{
57+
throw new Exception("Incorrect padding");
58+
}
59+
}
60+
61+
var result = new byte[contentLength];
62+
63+
Array.Copy(block, 0, result, 0, contentLength);
64+
65+
return result;
66+
}
67+
68+
public static byte ANSIX923_ValueProvider(int pos, int paddingLength, int blockLength)
69+
{
70+
return (byte)((pos == blockLength - 1) ? paddingLength : 0);
71+
}
72+
73+
public static byte[] ANSIX923_Remover(byte[] block)
74+
{
75+
var paddingLength = block[block.Length - 1];
76+
77+
if (paddingLength <= 1 && paddingLength >= block.Length)
78+
{
79+
throw new Exception("Incorrect padding");
80+
}
81+
82+
var contentLength = block.Length - paddingLength;
83+
84+
for (int i = block.Length - 2; i >= contentLength; --i)
85+
{
86+
if (block[i] != 0)
87+
{
88+
throw new Exception("Incorrect padding");
89+
}
90+
}
91+
92+
var result = new byte[contentLength];
93+
94+
Array.Copy(block, 0, result, 0, contentLength);
95+
96+
return result;
97+
}
98+
}
99+
}

PaddingValueProviders.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

Program.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using System.Security.Cryptography;
4+
using System.Text;
45
using Mono.Options;
56

67
namespace Padding_Oracle_Attack
@@ -9,21 +10,22 @@ class PaddingOracleAttack
910
{
1011
// change the padding used by the oracle below (decryptor uses the same as the server/oracle)
1112
private const PaddingMode paddingMode = PaddingMode.PKCS7;
12-
private static RemoteServerMock server = new RemoteServerMock(paddingMode);
13-
private static PaddingOracleDecryptor decryptor = new PaddingOracleDecryptor(server);
13+
private static RemoteServerMock oracle = new RemoteServerMock(paddingMode);
14+
private static PaddingOracleDecryptor decryptor = new PaddingOracleDecryptor(oracle);
15+
private static bool removePadding = true; // can be set to false by HandleConfigurationArguments
1416

1517
public static void Main(String[] args)
1618
{
1719
Console.WriteLine("~~ Padding Oracle Attack Demo ~~");
1820

1921
HandleConfigurationArguments(args);
2022

21-
Console.WriteLine("Oracle response delay set to {0} ms.", server.OracleDelayMilliseconds);
23+
Console.WriteLine("Oracle response delay set to {0} ms.", oracle.OracleDelayMilliseconds);
2224

2325
Console.WriteLine("\nEnter plaintext:");
2426
string plaintext = Console.ReadLine();
2527

26-
byte[] encrypted = server.Encrypt(plaintext);
28+
byte[] encrypted = oracle.Encrypt(plaintext);
2729
var blocks = ByteUtils.SliceIntoBlocks(encrypted);
2830

2931
Console.WriteLine("\nCiphertext blocks (base64):\n{0}", String.Join("\n", blocks.ConvertAll(block => Convert.ToBase64String(block))));
@@ -33,15 +35,22 @@ public static void Main(String[] args)
3335

3436
var stopwatch = new Stopwatch();
3537

36-
for (int blockIndex = 1; blockIndex < blocks.Count; ++blockIndex)
38+
var lastBlockIndex = blocks.Count - 1;
39+
for (int blockIndex = 1; blockIndex <= lastBlockIndex; ++blockIndex)
3740
{
3841
stopwatch.Start();
3942

40-
string decryptedPlaintext = decryptor.DecryptBlock(blocks[blockIndex], blocks[blockIndex - 1]);
43+
var decrypted = decryptor.DecryptBlock(blocks[blockIndex], blocks[blockIndex - 1]);
4144

4245
stopwatch.Stop();
4346

44-
Console.WriteLine(decryptedPlaintext[0] != 16 ? decryptedPlaintext : "(padding-only block)");
47+
if (removePadding && blockIndex == lastBlockIndex)
48+
{
49+
decrypted = PaddingUtils.GetPaddingRemoverFromMode(oracle.Padding).Invoke(decrypted);
50+
}
51+
52+
var decryptedPlaintext = Encoding.UTF8.GetString(decrypted, 0, decrypted.Length);
53+
Console.WriteLine(decryptedPlaintext.Length > 0 ? decryptedPlaintext : "(padding-only block)");
4554
}
4655

4756
var decodedBlocksCount = blocks.Count - 1;
@@ -57,7 +66,8 @@ public static void Main(String[] args)
5766
private static void HandleConfigurationArguments(String[] args)
5867
{
5968
OptionSet arguments = new OptionSet();
60-
arguments.Add("d|delay=", "oracle delay in milliseconds for each padding request", (uint d) => server.OracleDelayMilliseconds = d);
69+
arguments.Add("d|delay=", "oracle delay in milliseconds for each padding request", (uint d) => oracle.OracleDelayMilliseconds = d);
70+
arguments.Add("p|preserve-padding", "don't remove padding from decoded string", _ => removePadding = false);
6171
arguments.Add("h|help", "displays this message", _ =>
6272
{
6373
arguments.WriteOptionDescriptions(Console.Out);

0 commit comments

Comments
 (0)