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 |
} |