ZipAESTransform.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. //
  2. // ZipAESTransform.cs
  3. //
  4. // Copyright 2009 David Pierson
  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. #if !NET_1_1 && !NETCF_2_0
  38. // Framework version 2.0 required for Rfc2898DeriveBytes
  39. using System;
  40. using System.Security.Cryptography;
  41. namespace CommonMPQ.SharpZipLib.Encryption {
  42. /// <summary>
  43. /// Transforms stream using AES in CTR mode
  44. /// </summary>
  45. internal class ZipAESTransform : ICryptoTransform {
  46. private const int PWD_VER_LENGTH = 2;
  47. // WinZip use iteration count of 1000 for PBKDF2 key generation
  48. private const int KEY_ROUNDS = 1000;
  49. // For 128-bit AES (16 bytes) the encryption is implemented as expected.
  50. // For 256-bit AES (32 bytes) WinZip do full 256 bit AES of the nonce to create the encryption
  51. // block but use only the first 16 bytes of it, and discard the second half.
  52. private const int ENCRYPT_BLOCK = 16;
  53. private int _blockSize;
  54. private ICryptoTransform _encryptor;
  55. private readonly byte[] _counterNonce;
  56. private byte[] _encryptBuffer;
  57. private int _encrPos;
  58. private byte[] _pwdVerifier;
  59. private HMACSHA1 _hmacsha1;
  60. private bool _finalised;
  61. private bool _writeMode;
  62. /// <summary>
  63. /// Constructor.
  64. /// </summary>
  65. /// <param name="key">Password string</param>
  66. /// <param name="saltBytes">Random bytes, length depends on encryption strength.
  67. /// 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes.</param>
  68. /// <param name="blockSize">The encryption strength, in bytes eg 16 for 128 bits.</param>
  69. /// <param name="writeMode">True when creating a zip, false when reading. For the AuthCode.</param>
  70. ///
  71. public ZipAESTransform(string key, byte[] saltBytes, int blockSize, bool writeMode) {
  72. if (blockSize != 16 && blockSize != 32) // 24 valid for AES but not supported by Winzip
  73. throw new Exception("Invalid blocksize " + blockSize + ". Must be 16 or 32.");
  74. if (saltBytes.Length != blockSize / 2)
  75. throw new Exception("Invalid salt len. Must be " + blockSize / 2 + " for blocksize " + blockSize);
  76. // initialise the encryption buffer and buffer pos
  77. _blockSize = blockSize;
  78. _encryptBuffer = new byte[_blockSize];
  79. _encrPos = ENCRYPT_BLOCK;
  80. // Performs the equivalent of derive_key in Dr Brian Gladman's pwd2key.c
  81. Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(key, saltBytes, KEY_ROUNDS);
  82. RijndaelManaged rm = new RijndaelManaged();
  83. rm.Mode = CipherMode.ECB; // No feedback from cipher for CTR mode
  84. _counterNonce = new byte[_blockSize];
  85. byte[] byteKey1 = pdb.GetBytes(_blockSize);
  86. byte[] byteKey2 = pdb.GetBytes(_blockSize);
  87. _encryptor = rm.CreateEncryptor(byteKey1, byteKey2);
  88. _pwdVerifier = pdb.GetBytes(PWD_VER_LENGTH);
  89. //
  90. _hmacsha1 = new HMACSHA1(byteKey2);
  91. _writeMode = writeMode;
  92. }
  93. /// <summary>
  94. /// Implement the ICryptoTransform method.
  95. /// </summary>
  96. public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) {
  97. // Pass the data stream to the hash algorithm for generating the Auth Code.
  98. // This does not change the inputBuffer. Do this before decryption for read mode.
  99. if (!_writeMode) {
  100. _hmacsha1.TransformBlock(inputBuffer, inputOffset, inputCount, inputBuffer, inputOffset);
  101. }
  102. // Encrypt with AES in CTR mode. Regards to Dr Brian Gladman for this.
  103. int ix = 0;
  104. while (ix < inputCount) {
  105. if (_encrPos == ENCRYPT_BLOCK) {
  106. /* increment encryption nonce */
  107. int j = 0;
  108. while (++_counterNonce[j] == 0) {
  109. ++j;
  110. }
  111. /* encrypt the nonce to form next xor buffer */
  112. _encryptor.TransformBlock(_counterNonce, 0, _blockSize, _encryptBuffer, 0);
  113. _encrPos = 0;
  114. }
  115. outputBuffer[ix + outputOffset] = (byte)(inputBuffer[ix + inputOffset] ^ _encryptBuffer[_encrPos++]);
  116. //
  117. ix++;
  118. }
  119. if (_writeMode) {
  120. // This does not change the buffer.
  121. _hmacsha1.TransformBlock(outputBuffer, outputOffset, inputCount, outputBuffer, outputOffset);
  122. }
  123. return inputCount;
  124. }
  125. /// <summary>
  126. /// Returns the 2 byte password verifier
  127. /// </summary>
  128. public byte[] PwdVerifier {
  129. get {
  130. return _pwdVerifier;
  131. }
  132. }
  133. /// <summary>
  134. /// Returns the 10 byte AUTH CODE to be checked or appended immediately following the AES data stream.
  135. /// </summary>
  136. public byte[] GetAuthCode() {
  137. // We usually don't get advance notice of final block. Hash requres a TransformFinal.
  138. if (!_finalised) {
  139. byte[] dummy = new byte[0];
  140. _hmacsha1.TransformFinalBlock(dummy, 0, 0);
  141. _finalised = true;
  142. }
  143. return _hmacsha1.Hash;
  144. }
  145. #region ICryptoTransform Members
  146. /// <summary>
  147. /// Not implemented.
  148. /// </summary>
  149. public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) {
  150. throw new NotImplementedException("ZipAESTransform.TransformFinalBlock");
  151. }
  152. /// <summary>
  153. /// Gets the size of the input data blocks in bytes.
  154. /// </summary>
  155. public int InputBlockSize {
  156. get {
  157. return _blockSize;
  158. }
  159. }
  160. /// <summary>
  161. /// Gets the size of the output data blocks in bytes.
  162. /// </summary>
  163. public int OutputBlockSize {
  164. get {
  165. return _blockSize;
  166. }
  167. }
  168. /// <summary>
  169. /// Gets a value indicating whether multiple blocks can be transformed.
  170. /// </summary>
  171. public bool CanTransformMultipleBlocks {
  172. get {
  173. return true;
  174. }
  175. }
  176. /// <summary>
  177. /// Gets a value indicating whether the current transform can be reused.
  178. /// </summary>
  179. public bool CanReuseTransform {
  180. get {
  181. return true;
  182. }
  183. }
  184. /// <summary>
  185. /// Cleanup internal state.
  186. /// </summary>
  187. public void Dispose() {
  188. _encryptor.Dispose();
  189. }
  190. #endregion
  191. }
  192. }
  193. #endif