Statistics
| Branch: | Revision:

root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Encryption / PkzipClassic.cs @ 0eea575a

History | View | Annotate | Download (16.7 kB)

1
//
2
// PkzipClassic encryption
3
//
4
// Copyright 2004 John Reilly
5
//
6
// This program is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public License
8
// as published by the Free Software Foundation; either version 2
9
// of the License, or (at your option) any later version.
10
//
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
// GNU General Public License for more details.
15
//
16
// You should have received a copy of the GNU General Public License
17
// along with this program; if not, write to the Free Software
18
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
//
20
// Linking this library statically or dynamically with other modules is
21
// making a combined work based on this library.  Thus, the terms and
22
// conditions of the GNU General Public License cover the whole
23
// combination.
24
// 
25
// As a special exception, the copyright holders of this library give you
26
// permission to link this library with independent modules to produce an
27
// executable, regardless of the license terms of these independent
28
// modules, and to copy and distribute the resulting executable under
29
// terms of your choice, provided that you also meet, for each linked
30
// independent module, the terms and conditions of the license of that
31
// module.  An independent module is a module which is not derived from
32
// or based on this library.  If you modify this library, you may extend
33
// this exception to your version of the library, but you are not
34
// obligated to do so.  If you do not wish to do so, delete this
35
// exception statement from your version.
36
//
37

    
38
using System;
39
using System.Security.Cryptography;
40
using ICSharpCode.SharpZipLib.Silverlight.Checksums;
41

    
42
namespace ICSharpCode.SharpZipLib.Silverlight.Encryption
43
{
44
    /// <summary>
45
    /// PkzipClassic embodies the classic or original encryption facilities used in Pkzip archives.
46
    /// While it has been superceded by more recent and more powerful algorithms, its still in use and 
47
    /// is viable for preventing casual snooping
48
    /// </summary>
49
    public abstract class PkzipClassic : SymmetricAlgorithm
50
    {
51
        /// <summary>
52
        /// Generates new encryption keys based on given seed
53
        /// </summary>
54
        /// <param name="seed">The seed value to initialise keys with.</param>
55
        /// <returns>A new key value.</returns>
56
        public static byte[] GenerateKeys(byte[] seed)
57
        {
58
            if (seed == null)
59
            {
60
                throw new ArgumentNullException("seed");
61
            }
62

    
63
            if (seed.Length == 0)
64
            {
65
                throw new ArgumentException("Length is zero", "seed");
66
            }
67

    
68
            var newKeys = new uint[]{
69
                                        0x12345678,
70
                                        0x23456789,
71
                                        0x34567890
72
                                    };
73

    
74
            for (var i = 0; i < seed.Length; ++i)
75
            {
76
                newKeys[0] = Crc32.ComputeCrc32(newKeys[0], seed[i]);
77
                newKeys[1] = newKeys[1] + (byte) newKeys[0];
78
                newKeys[1] = newKeys[1]*134775813 + 1;
79
                newKeys[2] = Crc32.ComputeCrc32(newKeys[2], (byte) (newKeys[1] >> 24));
80
            }
81

    
82
            var result = new byte[12];
83
            result[0] = (byte) (newKeys[0] & 0xff);
84
            result[1] = (byte) ((newKeys[0] >> 8) & 0xff);
85
            result[2] = (byte) ((newKeys[0] >> 16) & 0xff);
86
            result[3] = (byte) ((newKeys[0] >> 24) & 0xff);
87
            result[4] = (byte) (newKeys[1] & 0xff);
88
            result[5] = (byte) ((newKeys[1] >> 8) & 0xff);
89
            result[6] = (byte) ((newKeys[1] >> 16) & 0xff);
90
            result[7] = (byte) ((newKeys[1] >> 24) & 0xff);
91
            result[8] = (byte) (newKeys[2] & 0xff);
92
            result[9] = (byte) ((newKeys[2] >> 8) & 0xff);
93
            result[10] = (byte) ((newKeys[2] >> 16) & 0xff);
94
            result[11] = (byte) ((newKeys[2] >> 24) & 0xff);
95
            return result;
96
        }
97
    }
98

    
99
    /// <summary>
100
    /// PkzipClassicCryptoBase provides the low level facilities for encryption
101
    /// and decryption using the PkzipClassic algorithm.
102
    /// </summary>
103
    internal class PkzipClassicCryptoBase
104
    {
105
        /// <summary>
106
        /// Transform a single byte 
107
        /// </summary>
108
        /// <returns>
109
        /// The transformed value
110
        /// </returns>
111
        protected byte TransformByte()
112
        {
113
            var temp = ((keys[2] & 0xFFFF) | 2);
114
            return (byte) ((temp*(temp ^ 1)) >> 8);
115
        }
116

    
117
        /// <summary>
118
        /// Set the key schedule for encryption/decryption.
119
        /// </summary>
120
        /// <param name="keyData">The data use to set the keys from.</param>
121
        protected void SetKeys(byte[] keyData)
122
        {
123
            if (keyData == null)
124
            {
125
                throw new ArgumentNullException("keyData");
126
            }
127

    
128
            if (keyData.Length != 12)
129
            {
130
                throw new InvalidOperationException("Key length is not valid");
131
            }
132

    
133
            keys = new uint[3];
134
            keys[0] = (uint) ((keyData[3] << 24) | (keyData[2] << 16) | (keyData[1] << 8) | keyData[0]);
135
            keys[1] = (uint) ((keyData[7] << 24) | (keyData[6] << 16) | (keyData[5] << 8) | keyData[4]);
136
            keys[2] = (uint) ((keyData[11] << 24) | (keyData[10] << 16) | (keyData[9] << 8) | keyData[8]);
137
        }
138

    
139
        /// <summary>
140
        /// Update encryption keys 
141
        /// </summary>		
142
        protected void UpdateKeys(byte ch)
143
        {
144
            keys[0] = Crc32.ComputeCrc32(keys[0], ch);
145
            keys[1] = keys[1] + (byte) keys[0];
146
            keys[1] = keys[1]*134775813 + 1;
147
            keys[2] = Crc32.ComputeCrc32(keys[2], (byte) (keys[1] >> 24));
148
        }
149

    
150
        /// <summary>
151
        /// Reset the internal state.
152
        /// </summary>
153
        protected void Reset()
154
        {
155
            keys[0] = 0;
156
            keys[1] = 0;
157
            keys[2] = 0;
158
        }
159

    
160
        #region Instance Fields
161

    
162
        private uint[] keys;
163

    
164
        #endregion
165
    }
166

    
167
    /// <summary>
168
    /// PkzipClassic CryptoTransform for encryption.
169
    /// </summary>
170
    internal class PkzipClassicEncryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform
171
    {
172
        /// <summary>
173
        /// Initialise a new instance of <see cref="PkzipClassicEncryptCryptoTransform"></see>
174
        /// </summary>
175
        /// <param name="keyBlock">The key block to use.</param>
176
        internal PkzipClassicEncryptCryptoTransform(byte[] keyBlock)
177
        {
178
            SetKeys(keyBlock);
179
        }
180

    
181
        #region ICryptoTransform Members
182

    
183
        /// <summary>
184
        /// Transforms the specified region of the specified byte array.
185
        /// </summary>
186
        /// <param name="inputBuffer">The input for which to compute the transform.</param>
187
        /// <param name="inputOffset">The offset into the byte array from which to begin using data.</param>
188
        /// <param name="inputCount">The number of bytes in the byte array to use as data.</param>
189
        /// <returns>The computed transform.</returns>
190
        public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
191
        {
192
            var result = new byte[inputCount];
193
            TransformBlock(inputBuffer, inputOffset, inputCount, result, 0);
194
            return result;
195
        }
196

    
197
        /// <summary>
198
        /// Transforms the specified region of the input byte array and copies 
199
        /// the resulting transform to the specified region of the output byte array.
200
        /// </summary>
201
        /// <param name="inputBuffer">The input for which to compute the transform.</param>
202
        /// <param name="inputOffset">The offset into the input byte array from which to begin using data.</param>
203
        /// <param name="inputCount">The number of bytes in the input byte array to use as data.</param>
204
        /// <param name="outputBuffer">The output to which to write the transform.</param>
205
        /// <param name="outputOffset">The offset into the output byte array from which to begin writing data.</param>
206
        /// <returns>The number of bytes written.</returns>
207
        public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer,
208
                                  int outputOffset)
209
        {
210
            for (var i = inputOffset; i < inputOffset + inputCount; ++i)
211
            {
212
                var oldbyte = inputBuffer[i];
213
                outputBuffer[outputOffset++] = (byte) (inputBuffer[i] ^ TransformByte());
214
                UpdateKeys(oldbyte);
215
            }
216
            return inputCount;
217
        }
218

    
219
        /// <summary>
220
        /// Gets a value indicating whether the current transform can be reused.
221
        /// </summary>
222
        public bool CanReuseTransform
223
        {
224
            get { return true; }
225
        }
226

    
227
        /// <summary>
228
        /// Gets the size of the input data blocks in bytes.
229
        /// </summary>
230
        public int InputBlockSize
231
        {
232
            get { return 1; }
233
        }
234

    
235
        /// <summary>
236
        /// Gets the size of the output data blocks in bytes.
237
        /// </summary>
238
        public int OutputBlockSize
239
        {
240
            get { return 1; }
241
        }
242

    
243
        /// <summary>
244
        /// Gets a value indicating whether multiple blocks can be transformed.
245
        /// </summary>
246
        public bool CanTransformMultipleBlocks
247
        {
248
            get { return true; }
249
        }
250

    
251
        /// <summary>
252
        /// Cleanup internal state.
253
        /// </summary>
254
        public void Dispose()
255
        {
256
            Reset();
257
        }
258

    
259
        #endregion
260
    }
261

    
262
    /// <summary>
263
    /// PkzipClassic CryptoTransform for decryption.
264
    /// </summary>
265
    internal class PkzipClassicDecryptCryptoTransform : PkzipClassicCryptoBase, ICryptoTransform
266
    {
267
        /// <summary>
268
        /// Initialise a new instance of <see cref="PkzipClassicDecryptCryptoTransform"></see>.
269
        /// </summary>
270
        /// <param name="keyBlock">The key block to decrypt with.</param>
271
        internal PkzipClassicDecryptCryptoTransform(byte[] keyBlock)
272
        {
273
            SetKeys(keyBlock);
274
        }
275

    
276
        #region ICryptoTransform Members
277

    
278
        /// <summary>
279
        /// Transforms the specified region of the specified byte array.
280
        /// </summary>
281
        /// <param name="inputBuffer">The input for which to compute the transform.</param>
282
        /// <param name="inputOffset">The offset into the byte array from which to begin using data.</param>
283
        /// <param name="inputCount">The number of bytes in the byte array to use as data.</param>
284
        /// <returns>The computed transform.</returns>
285
        public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
286
        {
287
            var result = new byte[inputCount];
288
            TransformBlock(inputBuffer, inputOffset, inputCount, result, 0);
289
            return result;
290
        }
291

    
292
        /// <summary>
293
        /// Transforms the specified region of the input byte array and copies 
294
        /// the resulting transform to the specified region of the output byte array.
295
        /// </summary>
296
        /// <param name="inputBuffer">The input for which to compute the transform.</param>
297
        /// <param name="inputOffset">The offset into the input byte array from which to begin using data.</param>
298
        /// <param name="inputCount">The number of bytes in the input byte array to use as data.</param>
299
        /// <param name="outputBuffer">The output to which to write the transform.</param>
300
        /// <param name="outputOffset">The offset into the output byte array from which to begin writing data.</param>
301
        /// <returns>The number of bytes written.</returns>
302
        public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer,
303
                                  int outputOffset)
304
        {
305
            for (var i = inputOffset; i < inputOffset + inputCount; ++i)
306
            {
307
                var newByte = (byte) (inputBuffer[i] ^ TransformByte());
308
                outputBuffer[outputOffset++] = newByte;
309
                UpdateKeys(newByte);
310
            }
311
            return inputCount;
312
        }
313

    
314
        /// <summary>
315
        /// Gets a value indicating whether the current transform can be reused.
316
        /// </summary>
317
        public bool CanReuseTransform
318
        {
319
            get { return true; }
320
        }
321

    
322
        /// <summary>
323
        /// Gets the size of the input data blocks in bytes.
324
        /// </summary>
325
        public int InputBlockSize
326
        {
327
            get { return 1; }
328
        }
329

    
330
        /// <summary>
331
        /// Gets the size of the output data blocks in bytes.
332
        /// </summary>
333
        public int OutputBlockSize
334
        {
335
            get { return 1; }
336
        }
337

    
338
        /// <summary>
339
        /// Gets a value indicating whether multiple blocks can be transformed.
340
        /// </summary>
341
        public bool CanTransformMultipleBlocks
342
        {
343
            get { return true; }
344
        }
345

    
346
        /// <summary>
347
        /// Cleanup internal state.
348
        /// </summary>
349
        public void Dispose()
350
        {
351
            Reset();
352
        }
353

    
354
        #endregion
355
    }
356

    
357
    /// <summary>
358
    /// Defines a wrapper object to access the Pkzip algorithm. 
359
    /// This class cannot be inherited.
360
    /// </summary>
361
    public sealed class PkzipClassicManaged : PkzipClassic
362
    {
363
        /// <summary>
364
        /// Get / set the applicable block size in bits.
365
        /// </summary>
366
        /// <remarks>The only valid block size is 8.</remarks>
367
        public override int BlockSize
368
        {
369
            get { return 8; }
370

    
371
            set
372
            {
373
                if (value != 8)
374
                {
375
                    throw new CryptographicException("Block size is invalid");
376
                }
377
            }
378
        }
379

    
380
        /// <summary>
381
        /// Get an array of legal <see cref="KeySizes">key sizes.</see>
382
        /// </summary>
383
        public override KeySizes[] LegalKeySizes
384
        {
385
            get
386
            {
387
                var keySizes = new KeySizes[1];
388
                keySizes[0] = new KeySizes(12*8, 12*8, 0);
389
                return keySizes;
390
            }
391
        }
392

    
393
        /// <summary>
394
        /// Get an array of legal <see cref="KeySizes">block sizes</see>.
395
        /// </summary>
396
        public override KeySizes[] LegalBlockSizes
397
        {
398
            get
399
            {
400
                var keySizes = new KeySizes[1];
401
                keySizes[0] = new KeySizes(1*8, 1*8, 0);
402
                return keySizes;
403
            }
404
        }
405

    
406
        /// <summary>
407
        /// Get / set the key value applicable.
408
        /// </summary>
409
        public override byte[] Key
410
        {
411
            get
412
            {
413
                if (key_ == null)
414
                {
415
                    GenerateKey();
416
                }
417

    
418
                return (byte[]) key_.Clone();
419
            }
420

    
421
            set
422
            {
423
                if (value == null)
424
                {
425
                    throw new ArgumentNullException("value");
426
                }
427

    
428
                if (value.Length != 12)
429
                {
430
                    throw new CryptographicException("Key size is illegal");
431
                }
432

    
433
                key_ = (byte[]) value.Clone();
434
            }
435
        }
436

    
437
        /// <summary>
438
        /// Generate an initial vector.
439
        /// </summary>
440
        public override void GenerateIV()
441
        {
442
            // Do nothing.
443
        }
444

    
445
        /// <summary>
446
        /// Generate a new random key.
447
        /// </summary>
448
        public override void GenerateKey()
449
        {
450
            key_ = new byte[12];
451
            var rnd = new Random();
452
            rnd.NextBytes(key_);
453
        }
454

    
455
        /// <summary>
456
        /// Create an encryptor.
457
        /// </summary>
458
        /// <param name="rgbKey">The key to use for this encryptor.</param>
459
        /// <param name="rgbIV">Initialisation vector for the new encryptor.</param>
460
        /// <returns>Returns a new PkzipClassic encryptor</returns>
461
        public override ICryptoTransform CreateEncryptor(
462
            byte[] rgbKey,
463
            byte[] rgbIV)
464
        {
465
            key_ = rgbKey;
466
            return new PkzipClassicEncryptCryptoTransform(Key);
467
        }
468

    
469
        /// <summary>
470
        /// Create a decryptor.
471
        /// </summary>
472
        /// <param name="rgbKey">Keys to use for this new decryptor.</param>
473
        /// <param name="rgbIV">Initialisation vector for the new decryptor.</param>
474
        /// <returns>Returns a new decryptor.</returns>
475
        public override ICryptoTransform CreateDecryptor(
476
            byte[] rgbKey,
477
            byte[] rgbIV)
478
        {
479
            key_ = rgbKey;
480
            return new PkzipClassicDecryptCryptoTransform(Key);
481
        }
482

    
483
        #region Instance Fields
484

    
485
        private byte[] key_;
486

    
487
        #endregion
488
    }
489
}