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