1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
using System;
using System.Text;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressor.Deflate;
namespace SharpCompress.Common.Zip
{
internal class PkwareTraditionalEncryptionData
{
private static readonly CRC32 crc32 = new CRC32();
private readonly UInt32[] _Keys = {0x12345678, 0x23456789, 0x34567890};
private PkwareTraditionalEncryptionData(string password)
{
Initialize(password);
}
private byte MagicByte
{
get
{
ushort t = (ushort) ((ushort) (_Keys[2] & 0xFFFF) | 2);
return (byte) ((t*(t ^ 1)) >> 8);
}
}
public static PkwareTraditionalEncryptionData ForRead(string password, ZipFileEntry header,
byte[] encryptionHeader)
{
var encryptor = new PkwareTraditionalEncryptionData(password);
byte[] plainTextHeader = encryptor.Decrypt(encryptionHeader, encryptionHeader.Length);
if (plainTextHeader[11] != (byte) ((header.Crc >> 24) & 0xff))
{
if (!FlagUtility.HasFlag(header.Flags, HeaderFlags.UsePostDataDescriptor))
{
throw new CryptographicException("The password did not match.");
}
if (plainTextHeader[11] != (byte) ((header.LastModifiedTime >> 8) & 0xff))
{
throw new CryptographicException("The password did not match.");
}
}
return encryptor;
}
public byte[] Decrypt(byte[] cipherText, int length)
{
if (length > cipherText.Length)
throw new ArgumentOutOfRangeException("length",
"Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array.");
var plainText = new byte[length];
for (int i = 0; i < length; i++)
{
var C = (byte) (cipherText[i] ^ MagicByte);
UpdateKeys(C);
plainText[i] = C;
}
return plainText;
}
public byte[] Encrypt(byte[] plainText, int length)
{
if (plainText == null)
throw new ArgumentNullException("plaintext");
if (length > plainText.Length)
throw new ArgumentOutOfRangeException("length",
"Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array.");
var cipherText = new byte[length];
for (int i = 0; i < length; i++)
{
byte C = plainText[i];
cipherText[i] = (byte) (plainText[i] ^ MagicByte);
UpdateKeys(C);
}
return cipherText;
}
private void Initialize(string password)
{
byte[] p = StringToByteArray(password);
for (int i = 0; i < password.Length; i++)
UpdateKeys(p[i]);
}
internal static byte[] StringToByteArray(string value, Encoding encoding)
{
byte[] a = encoding.GetBytes(value);
return a;
}
internal static byte[] StringToByteArray(string value)
{
return StringToByteArray(value, ArchiveEncoding.Password);
}
private void UpdateKeys(byte byteValue)
{
_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));
}
}
}
|