0

I have an encryption function based on the standard library AES with the block size of 16 bytes.

I'm encrypting a single block of size 16 bytes with a 16 byte key, using CryptoStream and MemoryStream.

If I Write and Flush, the result has 16 bytes. If I Write, Flush, and Close, it's 32 bytes.

Consider the example with the following encryption function:

public static byte[] EncryptBytes(byte[] data, string password, bool close)
{
    var myAes = Aes.Create();
    myAes.BlockSize = 128;
    myAes.IV = new byte[16];
    myAes.Key = Encoding.ASCII.GetBytes(password);
    var encryptor = myAes.CreateEncryptor(myAes.Key, myAes.IV);

    using var memoryStream = new MemoryStream();
    using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
    cryptoStream.Write(data, 0, data.Length);
    cryptoStream.Flush();
    if (close)
    {
        cryptoStream.Close(); // The offending line
    }
    byte[] cipher = memoryStream.ToArray();

    return cipher;
}

Then running the following program:

string key = "0123456789ABCDEF";
byte[] data = key.Select(c => (byte) c).ToArray();
string a = EncryptBytes(data, key, true).Aggregate("", (s, b) => s + b.ToString("X2"));
string b = EncryptBytes(data, key, false).Aggregate("", (s, b) => s + b.ToString("X2"));
Console.WriteLine($"A: {a}");
Console.WriteLine($"B: {b}");

Results in:

A: 8D835D0CFFD8BAD585FE8B4294C42188786F695CF204DC5AA109BA26653DFD97
B: 8D835D0CFFD8BAD585FE8B4294C42188

It's clear that A is the same as B for the first 16 bytes, but I would expect the B result and do not understand why the two are different.

Adam Streck
  • 340
  • 2
  • 15
  • 1
    You're using padding. If there's padding, it needs to be able to be removed. This means that there must be at least 1 byte of padding at the end of the encrypted output. But since the encrypted output must be a multiple of the block size (that's what the padding is for, after all) this means that if the input is an exact multiple of the block size, an entire block of padding must be added. When you close the `CryptoStream` it adds any padding and flushes the final block – canton7 Feb 03 '23 at 16:40
  • 1
    https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS#5_and_PKCS#7 – canton7 Feb 03 '23 at 16:42
  • @canton7 Understood, thank you for the explanation. I did not find any side-effects described in the documentation (https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptostream.close?view=netframework-1.1) , hence my confusion. – Adam Streck Feb 03 '23 at 16:48
  • Assuming you're properly `Dispose`ing you shouldn't have to close though. However it doesn't look like you are. In fact it looks like your `cryptoStream` is getting disposed as soon as it's created, so I'm not even sure why it's letting you continue to write to it. Am I missing something? – Emperor Eto Feb 03 '23 at 17:20
  • If I understand your question, the answer is that it's a "using declaration", which disposes in the inverse order at the end of the scope: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using – Adam Streck Feb 03 '23 at 23:03
  • @AdamStreck Read the Remarks on the type: https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptostream?view=netframework-1.1#remarks – canton7 Feb 04 '23 at 13:25

0 Answers0