a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
180 lines
5.3 KiB
C#
180 lines
5.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace SharpCompress.Common.Zip
|
|
{
|
|
internal class WinzipAesCryptoStream : Stream
|
|
{
|
|
private const int BLOCK_SIZE_IN_BYTES = 16;
|
|
private readonly SymmetricAlgorithm cipher;
|
|
private readonly byte[] counter = new byte[BLOCK_SIZE_IN_BYTES];
|
|
private readonly Stream stream;
|
|
private readonly ICryptoTransform transform;
|
|
private int nonce = 1;
|
|
private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES];
|
|
private bool isFinalBlock;
|
|
private long totalBytesLeftToRead;
|
|
private bool isDisposed;
|
|
|
|
internal WinzipAesCryptoStream(Stream stream, WinzipAesEncryptionData winzipAesEncryptionData, long length)
|
|
{
|
|
this.stream = stream;
|
|
totalBytesLeftToRead = length;
|
|
|
|
cipher = CreateCipher(winzipAesEncryptionData);
|
|
|
|
var iv = new byte[BLOCK_SIZE_IN_BYTES];
|
|
transform = cipher.CreateEncryptor(winzipAesEncryptionData.KeyBytes, iv);
|
|
}
|
|
|
|
private SymmetricAlgorithm CreateCipher(WinzipAesEncryptionData winzipAesEncryptionData)
|
|
{
|
|
RijndaelManaged cipher = new RijndaelManaged();
|
|
cipher.BlockSize = BLOCK_SIZE_IN_BYTES * 8;
|
|
cipher.KeySize = winzipAesEncryptionData.KeyBytes.Length * 8;
|
|
cipher.Mode = CipherMode.ECB;
|
|
cipher.Padding = PaddingMode.None;
|
|
return cipher;
|
|
}
|
|
|
|
public override bool CanRead
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override bool CanWrite
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
public override long Length
|
|
{
|
|
get { throw new NotImplementedException(); }
|
|
}
|
|
|
|
public override long Position
|
|
{
|
|
get { throw new NotImplementedException(); }
|
|
set { throw new NotImplementedException(); }
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (isDisposed)
|
|
{
|
|
return;
|
|
}
|
|
isDisposed = true;
|
|
if (disposing)
|
|
{
|
|
//read out last 10 auth bytes
|
|
var ten = new byte[10];
|
|
stream.Read(ten, 0, 10);
|
|
stream.Dispose();
|
|
}
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
if (totalBytesLeftToRead == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
int bytesToRead = count;
|
|
if (count > totalBytesLeftToRead)
|
|
{
|
|
bytesToRead = (int)totalBytesLeftToRead;
|
|
}
|
|
int read = stream.Read(buffer, offset, bytesToRead);
|
|
totalBytesLeftToRead -= read;
|
|
|
|
ReadTransformBlocks(buffer, offset, read);
|
|
|
|
return read;
|
|
}
|
|
|
|
private int ReadTransformOneBlock(byte[] buffer, int offset, int last)
|
|
{
|
|
if (isFinalBlock)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
int bytesRemaining = last - offset;
|
|
int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES)
|
|
? BLOCK_SIZE_IN_BYTES
|
|
: bytesRemaining;
|
|
|
|
// update the counter
|
|
Array.Copy(BitConverter.GetBytes(nonce++), 0, counter, 0, 4);
|
|
|
|
// Determine if this is the final block
|
|
if ((bytesToRead == bytesRemaining) && (totalBytesLeftToRead == 0))
|
|
{
|
|
counterOut = transform.TransformFinalBlock(counter,
|
|
0,
|
|
BLOCK_SIZE_IN_BYTES);
|
|
isFinalBlock = true;
|
|
}
|
|
else
|
|
{
|
|
transform.TransformBlock(counter,
|
|
0, // offset
|
|
BLOCK_SIZE_IN_BYTES,
|
|
counterOut,
|
|
0); // offset
|
|
}
|
|
|
|
XorInPlace(buffer, offset, bytesToRead);
|
|
return bytesToRead;
|
|
}
|
|
|
|
|
|
private void XorInPlace(byte[] buffer, int offset, int count)
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]);
|
|
}
|
|
}
|
|
|
|
private void ReadTransformBlocks(byte[] buffer, int offset, int count)
|
|
{
|
|
int posn = offset;
|
|
int last = count + offset;
|
|
|
|
while (posn < buffer.Length && posn < last)
|
|
{
|
|
int n = ReadTransformOneBlock(buffer, posn, last);
|
|
posn += n;
|
|
}
|
|
}
|
|
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
} |