Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ScopedBasicAlgorithm/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Packaging/DotNetZip/ZipCrypto.cs @ 14419

Last change on this file since 14419 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 16.7 KB
Line 
1// ZipCrypto.cs
2// ------------------------------------------------------------------
3//
4// Copyright (c) 2008, 2009, 2011 Dino Chiesa
5// All rights reserved.
6//
7// This code module is part of DotNetZip, a zipfile class library.
8//
9// ------------------------------------------------------------------
10//
11// This code is licensed under the Microsoft Public License.
12// See the file License.txt for the license details.
13// More info on: http://dotnetzip.codeplex.com
14//
15// ------------------------------------------------------------------
16//
17// last saved (in emacs):
18// Time-stamp: <2011-July-28 06:30:59>
19//
20// ------------------------------------------------------------------
21//
22// This module provides the implementation for "traditional" Zip encryption.
23//
24// Created Tue Apr 15 17:39:56 2008
25//
26// ------------------------------------------------------------------
27
28using System;
29
30namespace OfficeOpenXml.Packaging.Ionic.Zip
31{
32    /// <summary>
33    ///   This class implements the "traditional" or "classic" PKZip encryption,
34    ///   which today is considered to be weak. On the other hand it is
35    ///   ubiquitous. This class is intended for use only by the DotNetZip
36    ///   library.
37    /// </summary>
38    ///
39    /// <remarks>
40    ///   Most uses of the DotNetZip library will not involve direct calls into
41    ///   the ZipCrypto class.  Instead, the ZipCrypto class is instantiated and
42    ///   used by the ZipEntry() class when encryption or decryption on an entry
43    ///   is employed.  If for some reason you really wanted to use a weak
44    ///   encryption algorithm in some other application, you might use this
45    ///   library.  But you would be much better off using one of the built-in
46    ///   strong encryption libraries in the .NET Framework, like the AES
47    ///   algorithm or SHA.
48    /// </remarks>
49    internal class ZipCrypto
50    {
51        /// <summary>
52        ///   The default constructor for ZipCrypto.
53        /// </summary>
54        ///
55        /// <remarks>
56        ///   This class is intended for internal use by the library only. It's
57        ///   probably not useful to you. Seriously.  Stop reading this
58        ///   documentation.  It's a waste of your time.  Go do something else.
59        ///   Check the football scores. Go get an ice cream with a friend.
60        ///   Seriously.
61        /// </remarks>
62        ///
63        private ZipCrypto() { }
64
65        public static ZipCrypto ForWrite(string password)
66        {
67            ZipCrypto z = new ZipCrypto();
68            if (password == null)
69                throw new BadPasswordException("This entry requires a password.");
70            z.InitCipher(password);
71            return z;
72        }
73
74
75        public static ZipCrypto ForRead(string password, ZipEntry e)
76        {
77            System.IO.Stream s = e._archiveStream;
78            e._WeakEncryptionHeader = new byte[12];
79            byte[] eh = e._WeakEncryptionHeader;
80            ZipCrypto z = new ZipCrypto();
81
82            if (password == null)
83                throw new BadPasswordException("This entry requires a password.");
84
85            z.InitCipher(password);
86
87            ZipEntry.ReadWeakEncryptionHeader(s, eh);
88
89            // Decrypt the header.  This has a side effect of "further initializing the
90            // encryption keys" in the traditional zip encryption.
91            byte[] DecryptedHeader = z.DecryptMessage(eh, eh.Length);
92
93            // CRC check
94            // According to the pkzip spec, the final byte in the decrypted header
95            // is the highest-order byte in the CRC. We check it here.
96            if (DecryptedHeader[11] != (byte)((e._Crc32 >> 24) & 0xff))
97            {
98                // In the case that bit 3 of the general purpose bit flag is set to
99                // indicate the presence of an 'Extended File Header' or a 'data
100                // descriptor' (signature 0x08074b50), the last byte of the decrypted
101                // header is sometimes compared with the high-order byte of the
102                // lastmodified time, rather than the high-order byte of the CRC, to
103                // verify the password.
104                //
105                // This is not documented in the PKWare Appnote.txt.  It was
106                // discovered this by analysis of the Crypt.c source file in the
107                // InfoZip library http://www.info-zip.org/pub/infozip/
108                //
109                // The reason for this is that the CRC for a file cannot be known
110                // until the entire contents of the file have been streamed. This
111                // means a tool would have to read the file content TWICE in its
112                // entirety in order to perform PKZIP encryption - once to compute
113                // the CRC, and again to actually encrypt.
114                //
115                // This is so important for performance that using the timeblob as
116                // the verification should be the standard practice for DotNetZip
117                // when using PKZIP encryption. This implies that bit 3 must be
118                // set. The downside is that some tools still cannot cope with ZIP
119                // files that use bit 3.  Therefore, DotNetZip DOES NOT force bit 3
120                // when PKZIP encryption is in use, and instead, reads the stream
121                // twice.
122                //
123
124                if ((e._BitField & 0x0008) != 0x0008)
125                {
126                    throw new BadPasswordException("The password did not match.");
127                }
128                else if (DecryptedHeader[11] != (byte)((e._TimeBlob >> 8) & 0xff))
129                {
130                    throw new BadPasswordException("The password did not match.");
131                }
132
133                // We have a good password.
134            }
135            else
136            {
137                // A-OK
138            }
139            return z;
140        }
141
142
143
144
145        /// <summary>
146        /// From AppNote.txt:
147        /// unsigned char decrypt_byte()
148        ///     local unsigned short temp
149        ///     temp :=- Key(2) | 2
150        ///     decrypt_byte := (temp * (temp ^ 1)) bitshift-right 8
151        /// end decrypt_byte
152        /// </summary>
153        private byte MagicByte
154        {
155            get
156            {
157                UInt16 t = (UInt16)((UInt16)(_Keys[2] & 0xFFFF) | 2);
158                return (byte)((t * (t ^ 1)) >> 8);
159            }
160        }
161
162        // Decrypting:
163        // From AppNote.txt:
164        // loop for i from 0 to 11
165        //     C := buffer(i) ^ decrypt_byte()
166        //     update_keys(C)
167        //     buffer(i) := C
168        // end loop
169
170
171        /// <summary>
172        ///   Call this method on a cipher text to render the plaintext. You must
173        ///   first initialize the cipher with a call to InitCipher.
174        /// </summary>
175        ///
176        /// <example>
177        ///   <code>
178        ///     var cipher = new ZipCrypto();
179        ///     cipher.InitCipher(Password);
180        ///     // Decrypt the header.  This has a side effect of "further initializing the
181        ///     // encryption keys" in the traditional zip encryption.
182        ///     byte[] DecryptedMessage = cipher.DecryptMessage(EncryptedMessage);
183        ///   </code>
184        /// </example>
185        ///
186        /// <param name="cipherText">The encrypted buffer.</param>
187        /// <param name="length">
188        ///   The number of bytes to encrypt.
189        ///   Should be less than or equal to CipherText.Length.
190        /// </param>
191        ///
192        /// <returns>The plaintext.</returns>
193        public byte[] DecryptMessage(byte[] cipherText, int length)
194        {
195            if (cipherText == null)
196                throw new ArgumentNullException("cipherText");
197
198            if (length > cipherText.Length)
199                throw new ArgumentOutOfRangeException("length",
200                                                      "Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array.");
201
202            byte[] plainText = new byte[length];
203            for (int i = 0; i < length; i++)
204            {
205                byte C = (byte)(cipherText[i] ^ MagicByte);
206                UpdateKeys(C);
207                plainText[i] = C;
208            }
209            return plainText;
210        }
211
212        /// <summary>
213        ///   This is the converse of DecryptMessage.  It encrypts the plaintext
214        ///   and produces a ciphertext.
215        /// </summary>
216        ///
217        /// <param name="plainText">The plain text buffer.</param>
218        ///
219        /// <param name="length">
220        ///   The number of bytes to encrypt.
221        ///   Should be less than or equal to plainText.Length.
222        /// </param>
223        ///
224        /// <returns>The ciphertext.</returns>
225        public byte[] EncryptMessage(byte[] plainText, int length)
226        {
227            if (plainText == null)
228                throw new ArgumentNullException("plaintext");
229
230            if (length > plainText.Length)
231                throw new ArgumentOutOfRangeException("length",
232                                                      "Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array.");
233
234            byte[] cipherText = new byte[length];
235            for (int i = 0; i < length; i++)
236            {
237                byte C = plainText[i];
238                cipherText[i] = (byte)(plainText[i] ^ MagicByte);
239                UpdateKeys(C);
240            }
241            return cipherText;
242        }
243
244
245        /// <summary>
246        ///   This initializes the cipher with the given password.
247        ///   See AppNote.txt for details.
248        /// </summary>
249        ///
250        /// <param name="passphrase">
251        ///   The passphrase for encrypting or decrypting with this cipher.
252        /// </param>
253        ///
254        /// <remarks>
255        /// <code>
256        /// Step 1 - Initializing the encryption keys
257        /// -----------------------------------------
258        /// Start with these keys:
259        /// Key(0) := 305419896 (0x12345678)
260        /// Key(1) := 591751049 (0x23456789)
261        /// Key(2) := 878082192 (0x34567890)
262        ///
263        /// Then, initialize the keys with a password:
264        ///
265        /// loop for i from 0 to length(password)-1
266        ///     update_keys(password(i))
267        /// end loop
268        ///
269        /// Where update_keys() is defined as:
270        ///
271        /// update_keys(char):
272        ///   Key(0) := crc32(key(0),char)
273        ///   Key(1) := Key(1) + (Key(0) bitwiseAND 000000ffH)
274        ///   Key(1) := Key(1) * 134775813 + 1
275        ///   Key(2) := crc32(key(2),key(1) rightshift 24)
276        /// end update_keys
277        ///
278        /// Where crc32(old_crc,char) is a routine that given a CRC value and a
279        /// character, returns an updated CRC value after applying the CRC-32
280        /// algorithm described elsewhere in this document.
281        ///
282        /// </code>
283        ///
284        /// <para>
285        ///   After the keys are initialized, then you can use the cipher to
286        ///   encrypt the plaintext.
287        /// </para>
288        ///
289        /// <para>
290        ///   Essentially we encrypt the password with the keys, then discard the
291        ///   ciphertext for the password. This initializes the keys for later use.
292        /// </para>
293        ///
294        /// </remarks>
295        public void InitCipher(string passphrase)
296        {
297            byte[] p = SharedUtilities.StringToByteArray(passphrase);
298            for (int i = 0; i < passphrase.Length; i++)
299                UpdateKeys(p[i]);
300        }
301
302
303        private void UpdateKeys(byte byteValue)
304        {
305            _Keys[0] = (UInt32)crc32.ComputeCrc32((int)_Keys[0], byteValue);
306            _Keys[1] = _Keys[1] + (byte)_Keys[0];
307            _Keys[1] = _Keys[1] * 0x08088405 + 1;
308            _Keys[2] = (UInt32)crc32.ComputeCrc32((int)_Keys[2], (byte)(_Keys[1] >> 24));
309        }
310
311        ///// <summary>
312        ///// The byte array representing the seed keys used.
313        ///// Get this after calling InitCipher.  The 12 bytes represents
314        ///// what the zip spec calls the "EncryptionHeader".
315        ///// </summary>
316        //public byte[] KeyHeader
317        //{
318        //    get
319        //    {
320        //        byte[] result = new byte[12];
321        //        result[0] = (byte)(_Keys[0] & 0xff);
322        //        result[1] = (byte)((_Keys[0] >> 8) & 0xff);
323        //        result[2] = (byte)((_Keys[0] >> 16) & 0xff);
324        //        result[3] = (byte)((_Keys[0] >> 24) & 0xff);
325        //        result[4] = (byte)(_Keys[1] & 0xff);
326        //        result[5] = (byte)((_Keys[1] >> 8) & 0xff);
327        //        result[6] = (byte)((_Keys[1] >> 16) & 0xff);
328        //        result[7] = (byte)((_Keys[1] >> 24) & 0xff);
329        //        result[8] = (byte)(_Keys[2] & 0xff);
330        //        result[9] = (byte)((_Keys[2] >> 8) & 0xff);
331        //        result[10] = (byte)((_Keys[2] >> 16) & 0xff);
332        //        result[11] = (byte)((_Keys[2] >> 24) & 0xff);
333        //        return result;
334        //    }
335        //}
336
337        // private fields for the crypto stuff:
338        private UInt32[] _Keys = { 0x12345678, 0x23456789, 0x34567890 };
339        private Ionic.Crc.CRC32 crc32 = new Ionic.Crc.CRC32();
340
341    }
342
343    internal enum CryptoMode
344    {
345        Encrypt,
346        Decrypt
347    }
348
349    /// <summary>
350    ///   A Stream for reading and concurrently decrypting data from a zip file,
351    ///   or for writing and concurrently encrypting data to a zip file.
352    /// </summary>
353    internal class ZipCipherStream : System.IO.Stream
354    {
355        private ZipCrypto _cipher;
356        private System.IO.Stream _s;
357        private CryptoMode _mode;
358
359        /// <summary>  The constructor. </summary>
360        /// <param name="s">The underlying stream</param>
361        /// <param name="mode">To either encrypt or decrypt.</param>
362        /// <param name="cipher">The pre-initialized ZipCrypto object.</param>
363        public ZipCipherStream(System.IO.Stream s, ZipCrypto cipher, CryptoMode mode)
364            : base()
365        {
366            _cipher = cipher;
367            _s = s;
368            _mode = mode;
369        }
370
371        public override int Read(byte[] buffer, int offset, int count)
372        {
373            if (_mode == CryptoMode.Encrypt)
374                throw new NotSupportedException("This stream does not encrypt via Read()");
375
376            if (buffer == null)
377                throw new ArgumentNullException("buffer");
378
379            byte[] db = new byte[count];
380            int n = _s.Read(db, 0, count);
381            byte[] decrypted = _cipher.DecryptMessage(db, n);
382            for (int i = 0; i < n; i++)
383            {
384                buffer[offset + i] = decrypted[i];
385            }
386            return n;
387        }
388
389        public override void Write(byte[] buffer, int offset, int count)
390        {
391            if (_mode == CryptoMode.Decrypt)
392                throw new NotSupportedException("This stream does not Decrypt via Write()");
393
394            if (buffer == null)
395                throw new ArgumentNullException("buffer");
396
397            // workitem 7696
398            if (count == 0) return;
399
400            byte[] plaintext = null;
401            if (offset != 0)
402            {
403                plaintext = new byte[count];
404                for (int i = 0; i < count; i++)
405                {
406                    plaintext[i] = buffer[offset + i];
407                }
408            }
409            else plaintext = buffer;
410
411            byte[] encrypted = _cipher.EncryptMessage(plaintext, count);
412            _s.Write(encrypted, 0, encrypted.Length);
413        }
414
415
416        public override bool CanRead
417        {
418            get { return (_mode == CryptoMode.Decrypt); }
419        }
420        public override bool CanSeek
421        {
422            get { return false; }
423        }
424
425        public override bool CanWrite
426        {
427            get { return (_mode == CryptoMode.Encrypt); }
428        }
429
430        public override void Flush()
431        {
432            //throw new NotSupportedException();
433        }
434
435        public override long Length
436        {
437            get { throw new NotSupportedException(); }
438        }
439
440        public override long Position
441        {
442            get { throw new NotSupportedException(); }
443            set { throw new NotSupportedException(); }
444        }
445        public override long Seek(long offset, System.IO.SeekOrigin origin)
446        {
447            throw new NotSupportedException();
448        }
449
450        public override void SetLength(long value)
451        {
452            throw new NotSupportedException();
453        }
454    }
455}
Note: See TracBrowser for help on using the repository browser.