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