diff --git a/LiteDB.Tests/Internals/Aes_Tests.cs b/LiteDB.Tests/Internals/Aes_Tests.cs
index fe5489118..91c7031d3 100644
--- a/LiteDB.Tests/Internals/Aes_Tests.cs
+++ b/LiteDB.Tests/Internals/Aes_Tests.cs
@@ -1,5 +1,8 @@
-using System.IO;
+using System;
+using System.IO;
using System.Linq;
+using System.Reflection;
+using System.Security.Cryptography;
using FluentAssertions;
using LiteDB.Engine;
using Xunit;
@@ -61,5 +64,83 @@ public void Encrypt_Decrypt_Stream()
output2.All(x => x == 102).Should().BeTrue();
}
}
+
+ ///
+ /// Test whether AesStream can handle stream that has invalid page size.
+ ///
+ [Fact]
+ public void AesStream_Invalid_Page_Size()
+ {
+ var fakeContent = new byte[] {
+ 1,22,222,184,3,227,126,129,205,182,182,143,201,181,242,107,36,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 20,88,18,70,65,77,202,50,184,177,167,59,80,255,67,66,20,
+ 88,18,70,65,77,202,50,184,177,167,59,80,255,67,66
+ };
+
+ // stream of 64bytes (invalid page size)
+ using (var memoryStream = new MemoryStream())
+ {
+ memoryStream.Write(fakeContent, 0, fakeContent.Length);
+ memoryStream.Position = 0;
+
+ using (var crypto = new AesStream("password", memoryStream))
+ {
+ // 1st page is hidden, so AesStream.Length returns (stream.Length - PAGE_SIZE)
+ Assert.Equal(0, crypto.Length);
+
+ // AesStream should add padding to the underlying stream to make its size equivalent to PAGE_SIZE
+ Assert.Equal(8192, memoryStream.Length);
+ }
+ }
+ }
+
+ ///
+ /// Test whether AesStream can handle stream where bytes 32-64 are empty.
+ ///
+ [Fact]
+ public void AesStream_Invalid_Password()
+ {
+ // stream of 8192 bytes where bytes 32 to 64 is empty.
+ using (var memoryStream = new MemoryStream())
+ {
+ // 1st byte to indicate the stream is encrypted
+ memoryStream.WriteByte(1);
+
+ // next 16 bytes contain salt
+ var salt = new byte[16];
+ using (var rng = RandomNumberGenerator.Create())
+ {
+ rng.GetBytes(salt);
+ }
+ memoryStream.Write(salt, 0, salt.Length);
+
+ // remaining (8192 - 17) bytes are empty
+ var emptyContent = new byte[8175];
+ memoryStream.Write(emptyContent, 0, emptyContent.Length);
+
+ // reset the stream position to 0
+ memoryStream.Position = 0;
+
+ using (var crypto = new AesStream("password", memoryStream))
+ {
+ // 1st page is hidden, so AesStream.Length returns (stream.Length - PAGE_SIZE)
+ Assert.Equal(0, crypto.Length);
+
+ // AesStream should add padding to the underlying stream to make its size equivalent to PAGE_SIZE
+ Assert.Equal(8192, memoryStream.Length);
+
+ // AesStream should fill bytes 32-64 with encrypted 1s
+ var checkBytes = new byte[32];
+ var cryptoReader = typeof(AesStream)
+ .GetField("_reader", BindingFlags.Instance | BindingFlags.NonPublic)
+ .GetValue(crypto) as CryptoStream;
+
+ memoryStream.Position = 32;
+ cryptoReader.Read(checkBytes, 0, checkBytes.Length);
+ Assert.All(checkBytes, b => Assert.Equal(1, b));
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/LiteDB/Engine/Disk/Streams/AesStream.cs b/LiteDB/Engine/Disk/Streams/AesStream.cs
index 516cb752a..d0eb3f48f 100644
--- a/LiteDB/Engine/Disk/Streams/AesStream.cs
+++ b/LiteDB/Engine/Disk/Streams/AesStream.cs
@@ -62,9 +62,6 @@ public AesStream(string password, Stream stream)
// first byte =1 means this datafile is encrypted
_stream.WriteByte(1);
_stream.Write(this.Salt, 0, ENCRYPTION_SALT_SIZE);
-
- // fill with 0 full PAGE_SIZE
- _stream.Write(_emptyContent, 0, _emptyContent.Length);
}
else
{
@@ -109,6 +106,17 @@ public AesStream(string password, Stream stream)
var checkBuffer = new byte[32];
+ if (!isNew)
+ {
+ // check whether bytes 32 to 64 is empty. This indicates LiteDb was unable to write encrypted 1s during last attempt.
+ _stream.Read(checkBuffer, 0, checkBuffer.Length);
+ isNew = checkBuffer.All(x => x == 0);
+
+ // reset checkBuffer and stream position
+ Array.Clear(checkBuffer, 0, checkBuffer.Length);
+ _stream.Position = 32;
+ }
+
// fill checkBuffer with encrypted 1 to check when open
if (isNew)
{