Skip to content

Commit

Permalink
Move stream length check to try-catch
Browse files Browse the repository at this point in the history
  • Loading branch information
MihaZupan committed Sep 2, 2018
1 parent 5911413 commit aac2cab
Showing 1 changed file with 48 additions and 35 deletions.
83 changes: 48 additions & 35 deletions src/Telegram.Bot.Extensions.Passport/Decryption/Decrypter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
// ReSharper disable once CheckNamespace
namespace Telegram.Bot.Passport
{
/// <summary>
/// Default implementation of <see cref="IDecrypter"/>
/// </summary>
public class Decrypter : IDecrypter
{
/// <inheritdoc />
Expand Down Expand Up @@ -97,8 +100,6 @@ public Task DecryptFileAsync(
throw new ArgumentNullException(nameof(encryptedContent));
if (fileCredentials is null)
throw new ArgumentNullException(nameof(fileCredentials));
if (encryptedContent.Length % 16 != 0)
throw new PassportDataDecryptionException($"Invalid data length: {encryptedContent.Length}");
if (!destination.CanWrite)
throw new ArgumentException("Stream does not support writing.", nameof(destination));

Expand Down Expand Up @@ -143,45 +144,57 @@ CancellationToken cancellationToken
{
FindDataKeyAndIv(secret, hash, out byte[] dataKey, out byte[] dataIv);

using (var aes = Aes.Create())
try
{
// ReSharper disable once PossibleNullReferenceException
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.Key = dataKey;
aes.IV = dataIv;
aes.Padding = PaddingMode.None;

using (var decryptor = aes.CreateDecryptor())
using (CryptoStream aesStream = new CryptoStream(data, decryptor, CryptoStreamMode.Read))
using (var sha256 = SHA256.Create())
using (CryptoStream shaStream = new CryptoStream(aesStream, sha256, CryptoStreamMode.Read))
using (var aes = Aes.Create())
{
byte[] paddingBuffer = new byte[256];
int read = await shaStream.ReadAsync(paddingBuffer, 0, 256, cancellationToken)
.ConfigureAwait(false);

int paddingLength = paddingBuffer[0];
if (paddingLength < 32)
throw new PassportDataDecryptionException("Invalid padding size");

if (read < paddingLength)
throw new PassportDataDecryptionException("Invalid data");

await destination.WriteAsync(paddingBuffer, paddingLength, read - paddingLength, cancellationToken)
.ConfigureAwait(false);

await shaStream.CopyToAsync(destination, 81920, cancellationToken)
.ConfigureAwait(false);
// ReSharper disable once PossibleNullReferenceException
aes.KeySize = 256;
aes.Mode = CipherMode.CBC;
aes.Key = dataKey;
aes.IV = dataIv;
aes.Padding = PaddingMode.None;

byte[] paddedDataHash = sha256.Hash;
for (int i = 0; i < 32; i++)
using (var decryptor = aes.CreateDecryptor())
using (CryptoStream aesStream = new CryptoStream(data, decryptor, CryptoStreamMode.Read))
using (var sha256 = SHA256.Create())
using (CryptoStream shaStream = new CryptoStream(aesStream, sha256, CryptoStreamMode.Read))
{
if (hash[i] != paddedDataHash[i])
throw new PassportDataDecryptionException("Data hash mismatch");
byte[] paddingBuffer = new byte[256];
int read = await shaStream.ReadAsync(paddingBuffer, 0, 256, cancellationToken)
.ConfigureAwait(false);

int paddingLength = paddingBuffer[0];
if (paddingLength < 32)
throw new PassportDataDecryptionException("Invalid padding size");

if (read < paddingLength)
throw new PassportDataDecryptionException("Invalid data");

await destination.WriteAsync(paddingBuffer, paddingLength, read - paddingLength, cancellationToken)
.ConfigureAwait(false);

// 81920 is the default Stream.CopyTo buffer size
// The overload without the buffer size does not accept a cancellation token
await shaStream.CopyToAsync(destination, 81920, cancellationToken)
.ConfigureAwait(false);

byte[] paddedDataHash = sha256.Hash;
for (int i = 0; i < 32; i++)
{
if (hash[i] != paddedDataHash[i])
throw new PassportDataDecryptionException("Data hash mismatch");
}
}
}
}
catch (CryptographicException ce)
when (ce.Message.Equals("The input data is not a complete block.", StringComparison.Ordinal))
{
// The length check in this case can not be performed before decryption
// since we could be dealing with a stream that does not support seeking
throw new PassportDataDecryptionException("Invalid data length");
}
}

private static byte[] DecryptDataBytes(byte[] data, byte[] secret, byte[] hash)
Expand Down Expand Up @@ -238,7 +251,7 @@ private static byte[] DecryptDataBytes(byte[] data, byte[] secret, byte[] hash)

{
int paddingLength = dataWithPadding[0];
if (!(32 <= paddingLength && paddingLength < 256))
if (paddingLength < 32)
{
throw new PassportDataDecryptionException("Invalid data padding length");
}
Expand Down

0 comments on commit aac2cab

Please sign in to comment.