Skip to content

Commit f4967f0

Browse files
committed
block encryption changed to counter to avoid packet repeating attacks
1 parent 35d719f commit f4967f0

File tree

5 files changed

+140
-14
lines changed

5 files changed

+140
-14
lines changed

Blazer.Net.Tests/EncryptionTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Diagnostics;
23
using System.IO;
34
using System.Linq;
45
using System.Security.Cryptography;
@@ -148,5 +149,83 @@ public void Different_Paddings_Should_Not_Cause_Errors()
148149
Assert.That(len, Is.EqualTo(data.Length));
149150
CollectionAssert.AreEqual(data, data2.Take(data.Length));
150151
}
152+
153+
[Test]
154+
public void Compare_Random_And_CryptoRandom_Peformance()
155+
{
156+
var b = new byte[32];
157+
var crypto = RandomNumberGenerator.Create();
158+
var usual = new Random();
159+
var sw = Stopwatch.StartNew();
160+
var total = 0;
161+
while (sw.ElapsedMilliseconds < 1000)
162+
{
163+
crypto.GetBytes(b);
164+
total += b.Length;
165+
}
166+
167+
Console.WriteLine(total / sw.Elapsed.TotalSeconds);
168+
sw.Restart();
169+
170+
while (sw.ElapsedMilliseconds < 1000)
171+
{
172+
usual.NextBytes(b);
173+
total += b.Length;
174+
}
175+
176+
Console.WriteLine(total / sw.Elapsed.TotalSeconds);
177+
}
178+
179+
[Test]
180+
public void Check_Encrypt_Peformance()
181+
{
182+
var b = new byte[65530];
183+
var eh = new EncryptHelper("habahaba", 65536);
184+
var sw = Stopwatch.StartNew();
185+
var total = 0;
186+
while (sw.ElapsedMilliseconds < 1000)
187+
{
188+
eh.Encrypt(b, 0, b.Length);
189+
total += b.Length;
190+
}
191+
192+
Console.WriteLine(total / 1048576.0 / sw.Elapsed.TotalSeconds);
193+
sw.Restart();
194+
}
195+
196+
[Test]
197+
public void Duplicate_Data_Blocks_Should_Be_Catched_on_Decryption()
198+
{
199+
var data = Encoding.UTF8.GetBytes("some compressible not very long string. some some some.");
200+
var blazerCompressionOptions = BlazerCompressionOptions.CreateStream();
201+
blazerCompressionOptions.SetEncoderByAlgorithm(BlazerAlgorithm.NoCompress);
202+
blazerCompressionOptions.Password = "123";
203+
blazerCompressionOptions.IncludeFooter = false;
204+
blazerCompressionOptions.FlushMode = BlazerFlushMode.AutoFlush;
205+
var stream1 = new MemoryStream();
206+
var stream2 = new MemoryStream();
207+
var inps = new BlazerInputStream(stream1, blazerCompressionOptions);
208+
209+
// writing some test data
210+
inps.Write(new byte[100], 0, 100);
211+
var data1 = new byte[stream1.Length];
212+
stream1.Position = 0;
213+
stream1.Read(data1, 0, data1.Length);
214+
stream1.SetLength(0);
215+
// writing just one block
216+
inps.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 0 }, 0, 8);
217+
var data2 = new byte[stream1.Length];
218+
stream1.Position = 0;
219+
stream1.Read(data2, 0, data2.Length);
220+
stream1.SetLength(0);
221+
222+
stream2.Write(data1, 0, data1.Length);
223+
stream2.Write(data2, 0, data2.Length);
224+
// duplicated block
225+
stream2.Write(data2, 0, data2.Length);
226+
stream2.Position = 0;
227+
var os = new BlazerOutputStream(stream2, new BlazerDecompressionOptions("123"));
228+
Assert.Throws<InvalidOperationException>(() => os.CopyTo(new MemoryStream()), "Invalid encrypted block. Duplicated or damaged.");
229+
}
151230
}
152231
}

Blazer.Net/Blazer.Net.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
<Compile Include="Algorithms\BufferInfo.cs" />
6666
<Compile Include="BlazerFlushMode.cs" />
6767
<Compile Include="BlazerPatternedHelper.cs" />
68-
<Compile Include="Encyption\ISO10126TransformEmulator.cs" />
68+
<Compile Include="Encyption\Iso10126TransformEmulator.cs" />
6969
<Compile Include="Helpers\FileHeaderHelper.cs" />
7070
<Compile Include="Algorithms\StreamEncoderHigh.cs" />
7171
<Compile Include="Algorithms\Crc32C\Crc32C.cs" />

Blazer.Net/Encyption/DecryptHelper.cs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using System.Security.Cryptography;
6+
using System.Threading;
67

78
using Force.Blazer.Algorithms;
89

@@ -51,6 +52,10 @@ public override int GetHeaderLength()
5152
return 24;
5253
}
5354

55+
private long _counter;
56+
57+
private bool _useCounter;
58+
5459
public override void Init(byte[] buffer, int maxBlockSize)
5560
{
5661
if (buffer.Length != GetHeaderLength())
@@ -73,20 +78,45 @@ public override void Init(byte[] buffer, int maxBlockSize)
7378
{
7479
var toEncrypt = new byte[16];
7580
Buffer.BlockCopy(buffer, 8, toEncrypt, 0, 8);
76-
Buffer.BlockCopy(new[] { (byte)'B', (byte)'l', (byte)'a', (byte)'z', (byte)'e', (byte)'r', (byte)'!', (byte)'!' }, 0, toEncrypt, 8, 8);
81+
Buffer.BlockCopy(new[] { (byte)'B', (byte)'l', (byte)'a', (byte)'z', (byte)'e', (byte)'r', (byte)'!', (byte)'?' }, 0, toEncrypt, 8, 8);
7782
var encoded = encryptor.TransformFinalBlock(toEncrypt, 0, 16);
7883
if (encoded.Take(8).Where((t, i) => buffer[i + 16] != t).Any())
79-
throw new InvalidOperationException("Invalid password");
84+
{
85+
// trying second variant
86+
toEncrypt[15] = (byte)'!';
87+
encoded = encryptor.TransformFinalBlock(toEncrypt, 0, 16);
88+
if (encoded.Take(8).Where((t, i) => buffer[i + 16] != t).Any())
89+
{
90+
throw new InvalidOperationException("Invalid password");
91+
}
92+
else
93+
{
94+
_useCounter = false;
95+
}
96+
}
97+
else
98+
{
99+
_useCounter = true;
100+
_counter = 0;
101+
}
80102
}
81103
}
82104

83105
public override BufferInfo Decrypt(byte[] data, int offset, int length)
84106
{
85107
using (var decryptor = _aes.CreateDecryptor())
86108
{
87-
var cnt = decryptor.TransformBlock(data, offset, length - offset, _buffer, 0);
88-
// dummy data in header (8)
89-
return new BufferInfo(_buffer, 8, cnt);
109+
var ob = _buffer;
110+
var cnt = decryptor.TransformBlock(data, offset, length - offset, ob, 0);
111+
if (_useCounter)
112+
{
113+
var cv = ((long)ob[0] << 0) | ((long)ob[1] << 8) | ((long)ob[2] << 16) | ((long)ob[3] << 24) | ((long)ob[4] << 32) | ((long)ob[5] << 40) | ((long)ob[6] << 48) | ((long)ob[7] << 56);
114+
if (_counter++ != cv)
115+
throw new InvalidOperationException("Invalid encrypted block. Duplicated or damaged.");
116+
}
117+
118+
// else dummy data in header (8)
119+
return new BufferInfo(ob, 8, cnt);
90120
}
91121
}
92122

Blazer.Net/Encyption/EncryptHelper.cs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
using System.Diagnostics.CodeAnalysis;
33
using System.IO;
44
using System.Security.Cryptography;
5+
using System.Threading;
56

67
using Force.Blazer.Algorithms;
78

89
namespace Force.Blazer.Encyption
910
{
10-
internal class NullEncryptHelper
11+
public class NullEncryptHelper
1112
{
1213
public virtual BufferInfo Encrypt(byte[] data, int offset, int length)
1314
{
@@ -21,7 +22,7 @@ public virtual byte[] AppendHeader(byte[] header)
2122
}
2223

2324
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Reviewed. Suppression is OK here.")]
24-
internal class EncryptHelper : NullEncryptHelper
25+
public class EncryptHelper : NullEncryptHelper
2526
{
2627
private const int PrefixSize = 8;
2728

@@ -33,6 +34,8 @@ internal class EncryptHelper : NullEncryptHelper
3334

3435
private readonly RandomNumberGenerator _rng;
3536

37+
private readonly Random _random;
38+
3639
private readonly byte[] _buffer;
3740

3841
private readonly byte[] _randomBlock8;
@@ -56,6 +59,7 @@ public EncryptHelper(string password, int maxBufferSize)
5659
// this is fine for fast checking "is password correct", but does not
5760
// give full information about is it the required password
5861
_rng = RandomNumberGenerator.Create();
62+
_random = new Random();
5963
var salt = new byte[8];
6064
_rng.GetBytes(salt);
6165
var pass = new Rfc2898DeriveBytes(password, salt, PbkIterations);
@@ -64,7 +68,7 @@ public EncryptHelper(string password, int maxBufferSize)
6468
// zero. it is ok - we use data with salted random and do not need to use additional IV here
6569
_aes.IV = new byte[16];
6670
_aes.Mode = CipherMode.CBC;
67-
_aes.Padding = PaddingMode.Zeros; // other padding will add additional block, we manually will add random padding
71+
_aes.Padding = PaddingMode.None; // other padding will add additional block, we manually will add random padding
6872
_headerToWrite = new byte[24];
6973

7074
var random = new byte[8];
@@ -73,18 +77,26 @@ public EncryptHelper(string password, int maxBufferSize)
7377

7478
Buffer.BlockCopy(random, 0, toEncrypt, 0, 8);
7579

76-
Buffer.BlockCopy(new[] { (byte)'B', (byte)'l', (byte)'a', (byte)'z', (byte)'e', (byte)'r', (byte)'!', (byte)'!' }, 0, toEncrypt, 8, 8);
80+
Buffer.BlockCopy(new[] { (byte)'B', (byte)'l', (byte)'a', (byte)'z', (byte)'e', (byte)'r', (byte)'!', (byte)'?' }, 0, toEncrypt, 8, 8);
7781

7882
Buffer.BlockCopy(salt, 0, _headerToWrite, 0, 8);
7983
Buffer.BlockCopy(random, 0, _headerToWrite, 8, 8);
8084

85+
// currently, we use salt for password, so every encryption has own key, as result we do not need to use other values for counter
86+
// nonce is useful when password is static
87+
// _counter = ((long)salt[0] << 0) | ((long)salt[1] << 8) | ((long)salt[2] << 16) | ((long)salt[3] << 24) | ((long)salt[4] << 32) | ((long)salt[5] << 40) | ((long)salt[6] << 48) | ((long)salt[7] << 56);
88+
_counter = 0;
89+
8190
using (var encryptor = _aes.CreateEncryptor())
8291
{
8392
var encoded = encryptor.TransformFinalBlock(toEncrypt, 0, 16);
8493
Buffer.BlockCopy(encoded, 0, _headerToWrite, 16, 8);
8594
}
8695
}
8796

97+
private long _counter;
98+
99+
[SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1107:CodeMustNotContainMultipleStatementsOnOneLine", Justification = "Reviewed. Suppression is OK here.")]
88100
public override BufferInfo Encrypt(byte[] data, int offset, int length)
89101
{
90102
var count = length - offset;
@@ -93,16 +105,21 @@ public override BufferInfo Encrypt(byte[] data, int offset, int length)
93105
// this is will elimitate CBC problem with same blocks (if data is repeatable)
94106
using (var encryptor = _aes.CreateEncryptor())
95107
{
96-
_rng.GetBytes(_randomBlock8);
108+
// currently, we're not supporting multi-threading, so, we do not need to use Interlocked operations
109+
var c = _counter++;
110+
_buffer[0] = (byte)((c >> 00) & 0xff); _buffer[1] = (byte)((c >> 08) & 0xff); _buffer[2] = (byte)((c >> 16) & 0xff); _buffer[3] = (byte)((c >> 24) & 0xff);
111+
_buffer[4] = (byte)((c >> 32) & 0xff); _buffer[5] = (byte)((c >> 40) & 0xff); _buffer[6] = (byte)((c >> 48) & 0xff); _buffer[7] = (byte)((c >> 56) & 0xff);
112+
// _rng.GetBytes(_randomBlock8);
97113
// copying prefix
98-
Buffer.BlockCopy(_randomBlock8, 0, _buffer, 0, PrefixSize);
114+
// Buffer.BlockCopy(_randomBlock8, 0, _buffer, 0, PrefixSize);
99115

100116
// copying real data
101117
Buffer.BlockCopy(data, offset, _buffer, PrefixSize, count);
102118

103119
var addRandomCnt = 16 - ((count + PrefixSize) & 15);
104120
if (addRandomCnt < 16)
105121
{
122+
// here is no security required, but it faster
106123
_rng.GetBytes(_randomBlock16);
107124
Buffer.BlockCopy(_randomBlock16, 0, _buffer, PrefixSize + count, addRandomCnt);
108125
}

Blazer.Net/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@
3131
// You can specify all the values or you can default the Build and Revision Numbers
3232
// by using the '*' as shown below:
3333
// [assembly: AssemblyVersion("1.0.*")]
34-
[assembly: AssemblyVersion("0.9.4.0")]
35-
[assembly: AssemblyFileVersion("0.9.4.12")]
34+
[assembly: AssemblyVersion("0.10.0.0")]
35+
[assembly: AssemblyFileVersion("0.10.0.13")]

0 commit comments

Comments
 (0)