Files
sharpcompress/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs

112 lines
3.9 KiB
C#
Raw Normal View History

2015-12-30 11:19:42 +00:00
using System;
using System.Text;
using SharpCompress.Common.Zip.Headers;
2016-09-26 11:49:49 +01:00
using SharpCompress.Compressors.Deflate;
2015-12-30 11:19:42 +00:00
namespace SharpCompress.Common.Zip
{
internal class PkwareTraditionalEncryptionData
{
2018-05-06 08:59:00 +01:00
private static readonly CRC32 CRC32 = new CRC32();
private readonly UInt32[] _keys = {0x12345678, 0x23456789, 0x34567890};
private readonly ArchiveEncoding _archiveEncoding;
2015-12-30 11:19:42 +00:00
private PkwareTraditionalEncryptionData(string password, ArchiveEncoding archiveEncoding)
2015-12-30 11:19:42 +00:00
{
_archiveEncoding = archiveEncoding;
2015-12-30 11:19:42 +00:00
Initialize(password);
}
private byte MagicByte
{
get
{
2018-05-06 08:59:00 +01:00
ushort t = (ushort)((ushort)(_keys[2] & 0xFFFF) | 2);
return (byte)((t * (t ^ 1)) >> 8);
2015-12-30 11:19:42 +00:00
}
}
public static PkwareTraditionalEncryptionData ForRead(string password, ZipFileEntry header,
byte[] encryptionHeader)
{
var encryptor = new PkwareTraditionalEncryptionData(password, header.ArchiveEncoding);
2015-12-30 11:19:42 +00:00
byte[] plainTextHeader = encryptor.Decrypt(encryptionHeader, encryptionHeader.Length);
if (plainTextHeader[11] != (byte)((header.Crc >> 24) & 0xff))
2015-12-30 11:19:42 +00:00
{
if (!FlagUtility.HasFlag(header.Flags, HeaderFlags.UsePostDataDescriptor))
{
throw new CryptographicException("The password did not match.");
}
if (plainTextHeader[11] != (byte)((header.LastModifiedTime >> 8) & 0xff))
2015-12-30 11:19:42 +00:00
{
throw new CryptographicException("The password did not match.");
}
}
return encryptor;
}
public byte[] Decrypt(byte[] cipherText, int length)
{
if (length > cipherText.Length)
{
2017-07-27 11:05:33 -05:00
throw new ArgumentOutOfRangeException(nameof(length),
2015-12-30 11:19:42 +00:00
"Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array.");
}
2015-12-30 11:19:42 +00:00
var plainText = new byte[length];
for (int i = 0; i < length; i++)
{
2018-05-06 08:59:00 +01:00
var c = (byte)(cipherText[i] ^ MagicByte);
UpdateKeys(c);
plainText[i] = c;
2015-12-30 11:19:42 +00:00
}
return plainText;
}
public byte[] Encrypt(byte[] plainText, int length)
{
if (plainText == null)
{
2015-12-30 11:19:42 +00:00
throw new ArgumentNullException("plaintext");
}
2015-12-30 11:19:42 +00:00
if (length > plainText.Length)
{
2017-07-27 11:05:33 -05:00
throw new ArgumentOutOfRangeException(nameof(length),
2015-12-30 11:19:42 +00:00
"Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array.");
}
2015-12-30 11:19:42 +00:00
var cipherText = new byte[length];
for (int i = 0; i < length; i++)
{
2018-05-06 08:59:00 +01:00
byte c = plainText[i];
cipherText[i] = (byte)(plainText[i] ^ MagicByte);
2018-05-06 08:59:00 +01:00
UpdateKeys(c);
2015-12-30 11:19:42 +00:00
}
return cipherText;
}
private void Initialize(string password)
{
byte[] p = StringToByteArray(password);
for (int i = 0; i < password.Length; i++)
{
2015-12-30 11:19:42 +00:00
UpdateKeys(p[i]);
}
2015-12-30 11:19:42 +00:00
}
internal byte[] StringToByteArray(string value)
2015-12-30 11:19:42 +00:00
{
byte[] a = _archiveEncoding.Password.GetBytes(value);
2015-12-30 11:19:42 +00:00
return a;
}
private void UpdateKeys(byte byteValue)
{
2018-05-06 08:59:00 +01:00
_keys[0] = (UInt32)CRC32.ComputeCrc32((int)_keys[0], byteValue);
_keys[1] = _keys[1] + (byte)_keys[0];
_keys[1] = _keys[1] * 0x08088405 + 1;
_keys[2] = (UInt32)CRC32.ComputeCrc32((int)_keys[2], (byte)(_keys[1] >> 24));
2015-12-30 11:19:42 +00:00
}
}
2013-04-28 12:32:55 +01:00
}