@@ -6,19 +6,28 @@ package zip
6
6
7
7
import (
8
8
"bufio"
9
+ "bytes"
10
+ "crypto/aes"
11
+ "crypto/cipher"
12
+ "crypto/hmac"
13
+ "crypto/sha1"
9
14
"encoding/binary"
10
15
"errors"
11
16
"fmt"
12
17
"hash"
13
18
"hash/crc32"
14
19
"io"
20
+ "io/ioutil"
15
21
"os"
22
+
23
+ "golang.org/x/crypto/pbkdf2"
16
24
)
17
25
18
26
var (
19
- ErrFormat = errors .New ("zip: not a valid zip file" )
20
- ErrAlgorithm = errors .New ("zip: unsupported compression algorithm" )
21
- ErrChecksum = errors .New ("zip: checksum error" )
27
+ ErrFormat = errors .New ("zip: not a valid zip file" )
28
+ ErrAlgorithm = errors .New ("zip: unsupported compression algorithm" )
29
+ ErrChecksum = errors .New ("zip: checksum error" )
30
+ ErrDecryption = errors .New ("zip: decryption error" )
22
31
)
23
32
24
33
type Reader struct {
@@ -37,6 +46,32 @@ type File struct {
37
46
zipr io.ReaderAt
38
47
zipsize int64
39
48
headerOffset int64
49
+ password []byte
50
+ ae uint16
51
+ aesStrength byte
52
+ }
53
+
54
+ func aesKeyLen (strength byte ) int {
55
+ switch strength {
56
+ case 1 :
57
+ return aes128
58
+ case 2 :
59
+ return aes192
60
+ case 3 :
61
+ return aes256
62
+ default :
63
+ return 0
64
+ }
65
+ }
66
+
67
+ // SetPassword must be called before calling Open on the file.
68
+ func (f * File ) SetPassword (password []byte ) {
69
+ f .password = password
70
+ }
71
+
72
+ // IsEncrypted indicates whether this file's data is encrypted.
73
+ func (f * File ) IsEncrypted () bool {
74
+ return f .Flags & 0x1 == 1
40
75
}
41
76
42
77
func (f * File ) hasDataDescriptor () bool {
@@ -138,8 +173,17 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
138
173
if err != nil {
139
174
return
140
175
}
176
+ // If f is encrypted, CompressedSize64 includes salt, pwvv, encrypted data,
177
+ // and auth code lengths
141
178
size := int64 (f .CompressedSize64 )
142
- r := io .NewSectionReader (f .zipr , f .headerOffset + bodyOffset , size )
179
+ var r io.Reader
180
+ r = io .NewSectionReader (f .zipr , f .headerOffset + bodyOffset , size )
181
+ // check for encryption
182
+ if f .IsEncrypted () {
183
+ if r , err = newDecryptionReader (r , f ); err != nil {
184
+ return
185
+ }
186
+ }
143
187
dcomp := decompressor (f .Method )
144
188
if dcomp == nil {
145
189
err = ErrAlgorithm
@@ -150,6 +194,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
150
194
if f .hasDataDescriptor () {
151
195
desr = io .NewSectionReader (f .zipr , f .headerOffset + bodyOffset + size , dataDescriptorLen )
152
196
}
197
+ // TODO: if AE-2, skip CRC
153
198
rc = & checksumReader {
154
199
rc : rc ,
155
200
hash : crc32 .NewIEEE (),
@@ -159,6 +204,75 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
159
204
return
160
205
}
161
206
207
+ func newDecryptionReader (r io.Reader , f * File ) (io.ReadCloser , error ) {
208
+ keyLen := aesKeyLen (f .aesStrength )
209
+ saltLen := keyLen / 2 // salt is half of key len
210
+ if saltLen == 0 {
211
+ return nil , ErrDecryption
212
+ }
213
+
214
+ content := make ([]byte , f .CompressedSize64 )
215
+ if _ , err := io .ReadFull (r , content ); err != nil {
216
+ return nil , ErrDecryption
217
+ }
218
+
219
+ // grab the salt, pwvv, data, and authcode
220
+ salt := content [:saltLen ]
221
+ pwvv := content [saltLen : saltLen + 2 ]
222
+ content = content [saltLen + 2 :]
223
+ size := f .UncompressedSize64
224
+ data := content [:size ]
225
+ authcode := content [size :]
226
+
227
+ // generate keys
228
+ decKey , authKey , pwv := generateKeys (f .password , salt , keyLen )
229
+
230
+ // check password verifier (pwv)
231
+ if ! bytes .Equal (pwv , pwvv ) {
232
+ return nil , ErrDecryption
233
+ }
234
+
235
+ // check authentication
236
+ if ! checkAuthentication (data , authcode , authKey ) {
237
+ return nil , ErrDecryption
238
+ }
239
+
240
+ // set the IV
241
+ var iv [aes .BlockSize ]byte
242
+ iv [0 ] = 1
243
+
244
+ return decryptStream (data , decKey , iv [:]), nil
245
+ }
246
+
247
+ func decryptStream (ciphertext , key , iv []byte ) io.ReadCloser {
248
+ block , err := aes .NewCipher (key )
249
+ if err != nil {
250
+ return nil
251
+ }
252
+ stream := cipher .NewCTR (block , iv )
253
+ reader := cipher.StreamReader {S : stream , R : bytes .NewReader (ciphertext )}
254
+ return ioutil .NopCloser (reader )
255
+ }
256
+
257
+ func checkAuthentication (message , authcode , key []byte ) bool {
258
+ mac := hmac .New (sha1 .New , key )
259
+ mac .Write (message )
260
+ expectedAuthCode := mac .Sum (nil )
261
+ // Truncate at the first 10 bytes
262
+ expectedAuthCode = expectedAuthCode [:10 ]
263
+ return bytes .Equal (expectedAuthCode , authcode )
264
+ }
265
+
266
+ func generateKeys (password , salt []byte , keySize int ) (encKey , authKey , pwv []byte ) {
267
+ totalSize := (keySize * 2 ) + 2 // enc + auth + pv sizes
268
+
269
+ key := pbkdf2 .Key (password , salt , 1000 , totalSize , sha1 .New )
270
+ encKey = key [:keySize ]
271
+ authKey = key [keySize : keySize * 2 ]
272
+ pwv = key [keySize * 2 :]
273
+ return
274
+ }
275
+
162
276
type checksumReader struct {
163
277
rc io.ReadCloser
164
278
hash hash.Hash32
@@ -269,9 +383,10 @@ func readDirectoryHeader(f *File, r io.Reader) error {
269
383
if int (size ) > len (b ) {
270
384
return ErrFormat
271
385
}
272
- if tag == zip64ExtraId {
386
+ eb := readBuf (b [:size ])
387
+ switch tag {
388
+ case zip64ExtraId :
273
389
// update directory values from the zip64 extra block
274
- eb := readBuf (b [:size ])
275
390
if len (eb ) >= 8 {
276
391
f .UncompressedSize64 = eb .uint64 ()
277
392
}
@@ -281,6 +396,18 @@ func readDirectoryHeader(f *File, r io.Reader) error {
281
396
if len (eb ) >= 8 {
282
397
f .headerOffset = int64 (eb .uint64 ())
283
398
}
399
+ case winzipAesExtraId :
400
+ // grab the AE version
401
+ f .ae = eb .uint16 ()
402
+
403
+ // skip vendor ID
404
+ _ = eb .uint16 ()
405
+
406
+ // AES strength
407
+ f .aesStrength = eb .uint8 ()
408
+
409
+ // set the actual compression method.
410
+ f .Method = eb .uint16 ()
284
411
}
285
412
b = b [size :]
286
413
}
@@ -452,6 +579,12 @@ func findSignatureInBlock(b []byte) int {
452
579
453
580
type readBuf []byte
454
581
582
+ func (b * readBuf ) uint8 () byte {
583
+ v := (* b )[0 ]
584
+ * b = (* b )[1 :]
585
+ return v
586
+ }
587
+
455
588
func (b * readBuf ) uint16 () uint16 {
456
589
v := binary .LittleEndian .Uint16 (* b )
457
590
* b = (* b )[2 :]
0 commit comments