Compare commits

..

1 Commits

Author SHA1 Message Date
yeka 9a620c6cd3 Playing around 2016-10-16 19:50:27 +07:00
12 changed files with 785 additions and 268 deletions

20
LICENSE
View File

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (C) 2015 Alex Mullins
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,92 +0,0 @@
This fork add support for Standard Zip Encryption.
The work is based on https://github.com/alexmullins/zip
Available encryption:
```
zip.StandardEncryption
zip.AES128Encryption
zip.AES192Encryption
zip.AES256Encryption
```
## Warning
Zip Standard Encryption isn't actually secure.
Unless you have to work with it, please use AES encryption instead.
## Example Encrypt Zip
```
package main
import (
"bytes"
"io"
"log"
"os"
"git.internal/re/zip"
)
func main() {
contents := []byte("Hello World")
fzip, err := os.Create(`./test.zip`)
if err != nil {
log.Fatalln(err)
}
zipw := zip.NewWriter(fzip)
defer zipw.Close()
w, err := zipw.Encrypt(`test.txt`, `golang`, zip.AES256Encryption)
if err != nil {
log.Fatal(err)
}
_, err = io.Copy(w, bytes.NewReader(contents))
if err != nil {
log.Fatal(err)
}
zipw.Flush()
}
```
## Example Decrypt Zip
```
package main
import (
"fmt"
"io/ioutil"
"log"
"git.internal/re/zip"
)
func main() {
r, err := zip.OpenReader("encrypted.zip")
if err != nil {
log.Fatal(err)
}
defer r.Close()
for _, f := range r.File {
if f.IsEncrypted() {
f.SetPassword("12345")
}
r, err := f.Open()
if err != nil {
log.Fatal(err)
}
buf, err := ioutil.ReadAll(r)
if err != nil {
log.Fatal(err)
}
defer r.Close()
fmt.Printf("Size of %v: %v byte(s)\n", f.Name, len(buf))
}
}
```

77
README.txt Normal file
View File

@ -0,0 +1,77 @@
This is a fork of the Go archive/zip package to add support
for reading/writing password protected .zip files.
Only supports Winzip's AES extension: http://www.winzip.com/aes_info.htm.
This package DOES NOT intend to implement the encryption methods
mentioned in the original PKWARE spec (sections 6.0 and 7.0):
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
Status - Alpha. More tests and code clean up next.
Documentation -
https://godoc.org/github.com/alexmullins/zip
Roadmap
========
Reading - Done.
Writing - Done.
Testing - Needs more.
The process
============
1. hello.txt -> compressed -> encrypted -> .zip
2. .zip -> decrypted -> decompressed -> hello.txt
WinZip AES specifies
=====================
1. Encryption-Decryption w/ AES-CTR (128, 192, or 256 bits)
2. Key generation with PBKDF2-HMAC-SHA1 (1000 iteration count) that
generates a master key broken into the following:
a. First m bytes is for the encryption key
b. Next n bytes is for the authentication key
c. Last 2 bytes is the password verification value.
3. Following salt lengths are used w/ password during keygen:
------------------------------
AES Key Size | Salt Size
------------------------------
128bit(16bytes) | 8 bytes
192bit(24bytes) | 12 bytes
256bit(32bytes) | 16 bytes
-------------------------------
4. Master key len = AESKeyLen + AuthKeyLen + PWVLen:
a. AES 128 = 16 + 16 + 2 = 34 bytes of key material
b. AES 192 = 24 + 24 + 2 = 50 bytes of key material
c. AES 256 = 32 + 32 + 2 = 66 bytes of key material
5. Authentication Key is same size as AES key.
6. Authentication with HMAC-SHA1-80 (truncated to 80bits).
7. A new master key is generated for every file.
8. The file header and directory header compression method will
be 99 (decimal) indicating Winzip AES encryption. The actual
compression method will be in the extra's payload at the end
of the headers.
9. A extra field will be added to the file header and directory
header identified by the ID 0x9901 and contains the following info:
a. Header ID (2 bytes)
b. Data Size (2 bytes)
c. Vendor Version (2 bytes)
d. Vendor ID (2 bytes)
e. AES Strength (1 byte)
f. Compression Method (2 bytes)
10. The Data Size is always 7.
11. The Vendor Version can be either 0x0001 (AE-1) or
0x0002 (AE-2).
12. Vendor ID is ASCII "AE"
13. AES Strength:
a. 0x01 - AES-128
b. 0x02 - AES-192
c. 0x03 - AES-256
14. Compression Method is the actual compression method
used that was replaced by the encryption process mentioned in #8.
15. AE-1 keeps the CRC and should be verified after decompression.
AE-2 removes the CRC and shouldn't be verified after decompression.
Refer to http://www.winzip.com/aes_info.htm#winzip11 for the reasoning.
16. Storage Format (file data payload totals CompressedSize64 bytes):
a. Salt - 8, 12, or 16 bytes depending on keysize
b. Password Verification Value - 2 bytes
c. Encrypted Data - compressed size - salt - pwv - auth code lengths
d. Authentication code - 10 bytes

View File

@ -19,14 +19,7 @@ import (
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )
type EncryptionMethod int
const ( const (
StandardEncryption EncryptionMethod = 1
AES128Encryption EncryptionMethod = 2
AES192Encryption EncryptionMethod = 3
AES256Encryption EncryptionMethod = 4
// AES key lengths // AES key lengths
aes128 = 16 aes128 = 16
aes192 = 24 aes192 = 24
@ -386,14 +379,13 @@ func encryptStream(key []byte, w io.Writer) (io.Writer, error) {
// newEncryptionWriter returns an io.Writer that when written to, 1. writes // newEncryptionWriter returns an io.Writer that when written to, 1. writes
// out the salt, 2. writes out pwv, and 3. writes out authenticated, encrypted // out the salt, 2. writes out pwv, and 3. writes out authenticated, encrypted
// data. The authcode will be written out in fileWriter.close(). // data. The authcode will be written out in fileWriter.close().
func newEncryptionWriter(w io.Writer, password passwordFn, fw *fileWriter, aesstrength byte) (io.Writer, error) { func newEncryptionWriter(w io.Writer, password passwordFn, fw *fileWriter) (io.Writer, error) {
keysize := aesKeyLen(aesstrength) var salt [16]byte
salt := make([]byte, keysize / 2)
_, err := rand.Read(salt[:]) _, err := rand.Read(salt[:])
if err != nil { if err != nil {
return nil, errors.New("zip: unable to generate random salt") return nil, errors.New("zip: unable to generate random salt")
} }
ekey, akey, pwv := generateKeys(password(), salt[:], keysize) ekey, akey, pwv := generateKeys(password(), salt[:], aes256)
fw.hmac = hmac.New(sha1.New, akey) fw.hmac = hmac.New(sha1.New, akey)
aw := &authWriter{ aw := &authWriter{
hmac: fw.hmac, hmac: fw.hmac,
@ -432,23 +424,11 @@ func (h *FileHeader) writeWinZipExtra() {
eb.uint16(7) // following data size is 7 eb.uint16(7) // following data size is 7
eb.uint16(2) // ae 2 eb.uint16(2) // ae 2
eb.uint16(0x4541) // "AE" eb.uint16(0x4541) // "AE"
eb.uint8(h.aesStrength) // aes256 eb.uint8(3) // aes256
eb.uint16(h.Method) // original compression method eb.uint16(h.Method) // original compression method
h.Extra = append(h.Extra, buf[:]...) h.Extra = append(h.Extra, buf[:]...)
} }
func (h *FileHeader) setEncryptionMethod(enc EncryptionMethod) {
h.encryption = enc
switch enc {
case AES128Encryption:
h.aesStrength = 1
case AES192Encryption:
h.aesStrength = 2
case AES256Encryption:
h.aesStrength = 3
}
}
func (h *FileHeader) setEncryptionBit() { func (h *FileHeader) setEncryptionBit() {
h.Flags |= 0x1 h.Flags |= 0x1
} }
@ -463,6 +443,12 @@ func (h *FileHeader) SetPassword(password string) {
} }
} }
func (h *FileHeader) SetDecryptionPassword(password string) {
h.password = func() []byte {
return []byte(password)
}
}
// PasswordFn is a function that returns the password // PasswordFn is a function that returns the password
// as a byte slice // as a byte slice
type passwordFn func() []byte type passwordFn func() []byte
@ -472,12 +458,11 @@ type passwordFn func() []byte
// contents will be encrypted with AES-256 using the given password. The // contents will be encrypted with AES-256 using the given password. The
// file's contents must be written to the io.Writer before the next call // file's contents must be written to the io.Writer before the next call
// to Create, CreateHeader, or Close. // to Create, CreateHeader, or Close.
func (w *Writer) Encrypt(name string, password string, enc EncryptionMethod) (io.Writer, error) { func (w *Writer) Encrypt(name string, password string) (io.Writer, error) {
fh := &FileHeader{ fh := &FileHeader{
Name: name, Name: name,
Method: Deflate, Method: Deflate,
} }
fh.SetPassword(password) fh.SetDecryptionPassword(password)
fh.setEncryptionMethod(enc)
return w.CreateHeader(fh) return w.CreateHeader(fh)
} }

View File

@ -175,10 +175,9 @@ func TestPasswordWriteSimple(t *testing.T) {
contents := []byte("Hello World") contents := []byte("Hello World")
conLen := len(contents) conLen := len(contents)
for _, enc := range []EncryptionMethod{StandardEncryption, AES128Encryption, AES192Encryption, AES256Encryption} {
raw := new(bytes.Buffer) raw := new(bytes.Buffer)
zipw := NewWriter(raw) zipw := NewWriter(raw)
w, err := zipw.Encrypt("hello.txt", "golang", enc) w, err := zipw.Encrypt("hello.txt", "golang")
if err != nil { if err != nil {
t.Errorf("Expected to create a new FileHeader") t.Errorf("Expected to create a new FileHeader")
} }
@ -215,32 +214,3 @@ func TestPasswordWriteSimple(t *testing.T) {
t.Errorf("Expected the unzipped contents to equal '%s', but was '%s' instead", contents, buf.Bytes()) t.Errorf("Expected the unzipped contents to equal '%s', but was '%s' instead", contents, buf.Bytes())
} }
} }
}
func TestZipCrypto(t *testing.T) {
contents := []byte("Hello World")
conLen := len(contents)
raw := new(bytes.Buffer)
zipw := NewWriter(raw)
w, err := zipw.Encrypt("hello.txt", "golang", StandardEncryption)
if err != nil {
t.Errorf("Expected to create a new FileHeader")
}
n, err := io.Copy(w, bytes.NewReader(contents))
if err != nil || n != int64(conLen) {
t.Errorf("Expected to write the full contents to the writer.")
}
zipw.Close()
zipr, _ := NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len()))
zipr.File[0].SetPassword("golang")
r, _ := zipr.File[0].Open()
res := new(bytes.Buffer)
io.Copy(res, r)
r.Close()
if !bytes.Equal(contents, res.Bytes()) {
t.Errorf("Expected the unzipped contents to equal '%s', but was '%s' instead", contents, res.Bytes())
}
}

View File

@ -11,7 +11,7 @@ import (
"log" "log"
"os" "os"
"git.internal/re/zip" "github.com/alexmullins/zip"
) )
func ExampleWriter() { func ExampleWriter() {
@ -22,7 +22,7 @@ func ExampleWriter() {
w := zip.NewWriter(buf) w := zip.NewWriter(buf)
// Add some files to the archive. // Add some files to the archive.
files := []struct { var files = []struct {
Name, Body string Name, Body string
}{ }{
{"readme.txt", "This archive contains some text files."}, {"readme.txt", "This archive contains some text files."},
@ -81,7 +81,7 @@ func ExampleWriter_Encrypt() {
// write a password zip // write a password zip
raw := new(bytes.Buffer) raw := new(bytes.Buffer)
zipw := zip.NewWriter(raw) zipw := zip.NewWriter(raw)
w, err := zipw.Encrypt("hello.txt", "golang", zip.AES256Encryption) w, err := zipw.Encrypt("hello.txt", "golang")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -143,10 +143,11 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
size := int64(f.CompressedSize64) size := int64(f.CompressedSize64)
var r io.Reader var r io.Reader
rr := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) rr := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
// check for encryption // check for encryption
if f.IsEncrypted() { if f.IsEncrypted() {
if f.ae == 0 { if f.ae == 0 {
fmt.Printf("CRC32: %X ---- %v\n", f.CRC32, f.CRC32)
if r, err = ZipCryptoDecryptor(rr, f.password()); err != nil { if r, err = ZipCryptoDecryptor(rr, f.password()); err != nil {
return return
} }
@ -156,6 +157,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
} else { } else {
r = rr r = rr
} }
dcomp := decompressor(f.Method) dcomp := decompressor(f.Method)
if dcomp == nil { if dcomp == nil {
err = ErrAlgorithm err = ErrAlgorithm
@ -259,14 +261,21 @@ func readDirectoryHeader(f *File, r io.Reader) error {
f.CreatorVersion = b.uint16() f.CreatorVersion = b.uint16()
f.ReaderVersion = b.uint16() f.ReaderVersion = b.uint16()
f.Flags = b.uint16() f.Flags = b.uint16()
fmt.Printf("FLAG: %X\n", f.Flags)
f.Method = b.uint16() f.Method = b.uint16()
fmt.Printf("METHOD: %X\n", f.Method)
f.ModifiedTime = b.uint16() f.ModifiedTime = b.uint16()
f.ModifiedDate = b.uint16() f.ModifiedDate = b.uint16()
f.CRC32 = b.uint32() f.CRC32 = b.uint32()
fmt.Printf("Directory CRC: %X\n", f.CRC32)
f.CompressedSize = b.uint32() f.CompressedSize = b.uint32()
f.UncompressedSize = b.uint32() f.UncompressedSize = b.uint32()
f.CompressedSize64 = uint64(f.CompressedSize) f.CompressedSize64 = uint64(f.CompressedSize)
f.UncompressedSize64 = uint64(f.UncompressedSize) f.UncompressedSize64 = uint64(f.UncompressedSize)
fmt.Printf("ZIP Size 1: %v\n", f.CompressedSize)
fmt.Printf("ZIP Size 2: %v\n", f.CompressedSize64)
fmt.Printf("File Size 1: %v\n", f.UncompressedSize)
fmt.Printf("File Size 2: %v\n", f.UncompressedSize64)
filenameLen := int(b.uint16()) filenameLen := int(b.uint16())
extraLen := int(b.uint16()) extraLen := int(b.uint16())
commentLen := int(b.uint16()) commentLen := int(b.uint16())

View File

@ -101,7 +101,6 @@ type FileHeader struct {
// https://www.imperialviolet.org/2015/05/16/aeads.html // https://www.imperialviolet.org/2015/05/16/aeads.html
DeferAuth bool DeferAuth bool
encryption EncryptionMethod
password passwordFn // Returns the password to use when reading/writing password passwordFn // Returns the password to use when reading/writing
ae uint16 ae uint16
aesStrength byte aesStrength byte

32
temp/int.go Normal file
View File

@ -0,0 +1,32 @@
package main
import "fmt"
type Int int
func (i *Int) Incr(n int) Int {
*i = (*i) + Int(n)
return *i
}
func main() {
i := Int(10)
i.Incr(12)
fmt.Println(i)
b := &Bytes{b: []byte{1, 2, 3, 4, 5}, i: 0}
c := b.Get(0, 3)
c[1] = 11
fmt.Println(b)
fmt.Println(c)
j := &Bytes{b: []byte{1, 2, 3, 4, 5}, i: 0}
k := j.IGet(3)
l := j.IGet(2)
k[0] = 10
l[0] = 11
fmt.Println(j)
fmt.Println(k)
fmt.Println(l)
}

544
temp/play.go Normal file
View File

@ -0,0 +1,544 @@
package main
import (
zip2 "github.com/yeka/zip"
"bytes"
"os"
"io"
"fmt"
"archive/zip"
"log"
"hash/crc32"
"encoding/binary"
"io/ioutil"
)
func main() {
//fmt.Printf("%X\n", crc32.Update(0, crc32.IEEETable, []byte{0}))
//return
password := "zipcrypto"
new(ZipReader).FromBytes(createzip(password), password)
new(ZipReader).Open("java2.zip", password) // FA176C7F
os.Remove("myzip/test1.zip")
ioutil.WriteFile("myzip/test1.zip", createzip(password), os.ModePerm)
return
//z.Open("java2.zip", "zipcrypto") // FA176C7F
//z.Open("java.zip", "123")
//z.Open("a.zip")
//z.Open("test.zip")
fmt.Println("=======================================")
return
//unknown1()
//show("java.zip", "zipcrypto")
zipunzip()
return
fmt.Printf("%X\n", 878082192)
show("banyak.zip", "123")
//show("pass.zip", "zipcrypto")
//show("java.zip", "zipcrypto")
//show("a.zip", "aa") // Incorrect password
//show("test.zip", "")
fmt.Printf("%b\n", zip2.Crc32update(0, 0))
h := crc32.NewIEEE()
h.Write([]byte{0})
fmt.Printf("%b\n", h.Sum32())
fmt.Printf("%b\n", crc32.Update(0, crc32.IEEETable, []byte{0}))
}
func play1() {
de := []byte("hello")
//e := zip.NewZipCrypto([]byte("Passwordnya panjang banget kk"))
//en := e.EncryptMessage([]byte("Hello Worlds Muahaha"))
//d := zip.NewZipCrypto([]byte("Passwordnya panjang banget kk"))
//de = d.DecryptMessage(en)
fmt.Println(string(de))
os.Exit(0)
buf := new(bytes.Buffer)
//a := zip.NewWriter(buf)
//w, _ := a.Create("a.txt")
//w.Write([]byte(`Hello World`))
//a.Close()
f, _ := os.Create("temp/test.zip")
io.Copy(f, buf)
f.Close()
_ = zip.Deflate
}
func show(filename string, password string) {
r, err := zip2.OpenReader(filename)
if err != nil {
log.Fatal(err)
}
defer r.Close()
for _, f := range r.File {
fmt.Printf("------ %s ------\n", f.Name)
f.SetDecryptionPassword(password)
rc, err := f.Open()
if err != nil {
fmt.Println("Opening error")
log.Print(err)
continue
}
buf := new(bytes.Buffer)
if _, err = io.Copy(buf, rc); err != nil {
fmt.Println("buffering error")
log.Print(err)
continue
}
fmt.Printf("Size: %d\nContent:\n", buf.Len())
fmt.Println(string(buf.Bytes()))
//fmt.Println(buf.Bytes())
rc.Close()
fmt.Println()
fmt.Println()
}
}
func createzip(password string) []byte {
fmt.Printf("\n\n========================== Writing ========================== \n\n")
mydata, _ := ioutil.ReadFile("a.java")
b := new(bytes.Buffer)
z := zip2.NewWriter(b)
//w, _ := z.Create("a.java")
w, _ := z.Encrypt("a.java", password)
fmt.Println(w.Write(mydata))
z.Close()
return b.Bytes()
}
func zipunzip() {
fmt.Printf("\n\n========================== Writing ========================== \n\n")
mydata := []byte(`Hello World`)
password := "golang"
b := new(bytes.Buffer)
z := zip2.NewWriter(b)
//w, _ := z.Create("a.txt")
w, _ := z.Encrypt("a.txt", password)
fmt.Println(w.Write(mydata))
z.Close()
fmt.Printf("\n\n========================== Reading ========================== \n\n")
rz, _ := zip2.NewReader(bytes.NewReader(b.Bytes()), int64(b.Len()))
rz.File[0].SetDecryptionPassword(password)
r, _ := rz.File[0].Open()
myresult := new(bytes.Buffer)
io.Copy(myresult, r)
r.Close()
fmt.Println(mydata)
fmt.Println(myresult.Bytes())
fmt.Println(0 == bytes.Compare(mydata, myresult.Bytes()))
}
func unknown1() {
hello := []byte("Hello world\n")
b := new(bytes.Buffer)
l := &zipwriterboongan{b}
c := &zipwriterboongan{l}
//m := &minues{l}
//c := &minues2{m}
//
////i := io.MultiWriter(b, m, l, c)
//c.Write(hello)
c.Write(hello)
fmt.Println(string(b.Bytes()))
}
type zipwriterboongan struct {
w io.Writer
}
func (m *zipwriterboongan) Write(p []byte) (i int, err error) {
temp := make([]byte, len(p))
for i, b := range p {
temp[i] = b ^ 0xFF
}
m.w.Write(temp)
return
}
type ftpwriterboongan struct {
w io.Writer
}
func (m *ftpwriterboongan) Write(p []byte) (i int, err error) {
temp := make([]byte, len(p))
for i, b := range p {
temp[i] = b ^ 0xFF
}
m.w.Write(temp)
return
}
type ZipReader struct {
b *Bytes
password []byte
files []*ZipFile
central_dir []*ZipCentralDirectory
dir_end *ZipDirectoryEnd
}
type ZipFile struct {
zip *ZipReader
header *ZipFileHeader
data *ZipFileData
descriptor *ZipFileDescriptor
}
func (zf *ZipFile) init(offset, datasize uint64) {
h := &ZipFileHeader{file: zf}
h.init(offset)
f := &ZipFileData{file: zf}
f.content = zf.zip.b.IGet(datasize)
e := &ZipFileDescriptor{file: zf}
if u16x(h.general_purpose_bit_flag) & 8 > 0 {
e.init(zf.zip.b.Pos())
}
zf.header = h
zf.data = f
zf.descriptor = e
}
type ZipFileHeader struct {
file *ZipFile
local_file_header_signature []byte
version_needed_to_extract []byte
general_purpose_bit_flag []byte
compression_method []byte
last_mod_file_time []byte
last_mod_file_date []byte
crc_32 []byte
compressed_size []byte
uncompressed_size []byte
filename_length []byte
extra_field_length []byte
filename []byte
extra_field []byte
}
func (fh *ZipFileHeader) init(offset uint64) {
b := fh.file.zip.b
b.Offset(offset)
fh.local_file_header_signature = b.IGet(4)
fh.version_needed_to_extract = b.IGet(2)
fh.general_purpose_bit_flag = b.IGet(2)
fh.compression_method = b.IGet(2)
fh.last_mod_file_time = b.IGet(2)
fh.last_mod_file_date = b.IGet(2)
fh.crc_32 = b.IGet(4)
fh.compressed_size = b.IGet(4)
fh.uncompressed_size = b.IGet(4)
fh.filename_length = b.IGet(2)
fh.extra_field_length = b.IGet(2)
fh.filename = b.IGet(u16x(fh.filename_length))
fh.extra_field = b.IGet(u16x(fh.extra_field_length))
}
type ZipFileData struct {
file *ZipFile
content []byte
}
type ZipFileDescriptor struct {
file *ZipFile
signature []byte
crc_32 []byte
compressed_size []byte
uncompressed_size []byte
}
func (fd *ZipFileDescriptor) init(offset uint64) {
b := fd.file.zip.b
b.Offset(offset)
sig := b.IGet(4)
if u32x(sig) == 0x08074b50 {
fd.signature = sig
fd.crc_32 = b.IGet(4)
} else {
fd.crc_32 = sig
}
fd.compressed_size = b.IGet(4)
fd.uncompressed_size = b.IGet(4)
}
type ZipCentralDirectory struct {
zip *ZipReader
central_file_header_signature []byte
version_made_by []byte
version_needed_to_extrac []byte
general_purpose_bit_flag []byte
compression_method []byte
last_mod_file_time []byte
last_mod_file_date []byte
crc_32 []byte
compressed_size []byte
uncompressed_size []byte
filename_length []byte
extra_field_length []byte
file_comment_length []byte
disk_num_start []byte
internal_file_attr []byte
external_file_attr []byte
local_header_relative_offset []byte
filename []byte
extra_field []byte
file_comment []byte
}
func (cd *ZipCentralDirectory) init(offset uint64) (size uint64) {
cd.zip.b.Offset(offset)
cd.central_file_header_signature = cd.zip.b.IGet(4)
cd.version_made_by = cd.zip.b.IGet(2)
cd.version_needed_to_extrac = cd.zip.b.IGet(2)
cd.general_purpose_bit_flag = cd.zip.b.IGet(2)
cd.compression_method = cd.zip.b.IGet(2)
cd.last_mod_file_time = cd.zip.b.IGet(2)
cd.last_mod_file_date = cd.zip.b.IGet(2)
cd.crc_32 = cd.zip.b.IGet(4)
cd.compressed_size = cd.zip.b.IGet(4)
cd.uncompressed_size = cd.zip.b.IGet(4)
cd.filename_length = cd.zip.b.IGet(2)
cd.extra_field_length = cd.zip.b.IGet(2)
cd.file_comment_length = cd.zip.b.IGet(2)
cd.disk_num_start = cd.zip.b.IGet(2)
cd.internal_file_attr = cd.zip.b.IGet(2)
cd.external_file_attr = cd.zip.b.IGet(4)
cd.local_header_relative_offset = cd.zip.b.IGet(4)
cd.filename = cd.zip.b.IGet(u16x(cd.filename_length))
cd.extra_field = cd.zip.b.IGet(u16x(cd.extra_field_length))
cd.file_comment = cd.zip.b.IGet(u16x(cd.file_comment_length))
size = 46 + u16x(cd.filename_length) + u16x(cd.extra_field_length) + u16x(cd.file_comment_length)
return
}
type ZipDirectoryEnd struct {
zip *ZipReader
signature []byte
disknum []byte
disknum_start_of_central_directory []byte
entries_in_this_disk []byte
num_of_entries []byte
size_of_central_dir []byte
offset_of_central_dir []byte
zipfile_comment_length []byte
zipfile_comment []byte
}
func (zde *ZipDirectoryEnd) init(offset uint64) {
zde.zip.b.Offset(offset)
zde.signature = zde.zip.b.IGet(4)
zde.disknum = zde.zip.b.IGet(2)
zde.disknum_start_of_central_directory = zde.zip.b.IGet(2)
zde.entries_in_this_disk = zde.zip.b.IGet(2)
zde.num_of_entries = zde.zip.b.IGet(2)
zde.size_of_central_dir = zde.zip.b.IGet(4)
zde.offset_of_central_dir = zde.zip.b.IGet(4)
zde.zipfile_comment_length = zde.zip.b.IGet(2)
zde.zipfile_comment = zde.zip.b.IGet(u16x(zde.zipfile_comment_length))
}
func (z *ZipReader) FromBytes(buf []byte, password string) {
z.b = &Bytes{buf, 0}
z.password = []byte(password)
z.Init()
}
func (z *ZipReader) Open(filename string, password string) {
buf, _ := ioutil.ReadFile(filename)
z.FromBytes(buf, password)
}
func (z *ZipReader) Init() {
// First find Directory End
var i uint64
for i = uint64(len(z.b.b)) - 22; i >= 0; i-- {
if z.b.BGet(i, 4).I64() == 0x06054b50 {
z.dir_end = &ZipDirectoryEnd{zip: z}
z.dir_end.init(i)
break
}
}
// Read all Central Directory Header
central_dir_offset := u16x(z.dir_end.offset_of_central_dir)
for i = 0; i < u16x(z.dir_end.num_of_entries); i++ {
cdfh := &ZipCentralDirectory{zip: z}
central_dir_offset += cdfh.init(central_dir_offset)
z.central_dir = append(z.central_dir, cdfh)
}
for i = 0; i < u16x(z.dir_end.num_of_entries); i++ {
zf := &ZipFile{zip: z}
zf.init(u32x(z.central_dir[i].local_header_relative_offset), u32x(z.central_dir[i].compressed_size))
z.files = append(z.files, zf)
}
fmt.Printf("%0 X\n", z.files[0].header)
fmt.Printf("%0 X\n", z.files[0].descriptor)
// PlayGround
//fmt.Printf("%v\n", z.files[0].data.content)
//zc := zip2.NewZipCrypto(z.password)
//buf := zc.DecryptMessage(z.files[0].data.content)
////fmt.Printf("%v\n", buf)
//rc := flate.NewReader(bytes.NewBuffer(buf[12:]))
//defer rc.Close()
//res, err := ioutil.ReadAll(rc)
//if err != nil {
// fmt.Println(err.Error())
//}
//fmt.Printf("%v\n", string(res))
//fmt.Printf("---------------- %v ----------------\n", string(h.filename))
//fmt.Printf("Signature: %02X\n", h.local_file_header_signature)
//fmt.Printf("Version: %v\n", h.version_needed_to_extract)
//fmt.Printf("General Purpose Bit Flag: %08b\n", h.general_purpose_bit_flag)
//fmt.Printf("Compression Method: %v\n", h.compression_method)
//fmt.Printf("CRC32: %0 X, Compressed: %v, Uncompressed: %v\n", h.crc_32, u16x(h.compressed_size), u16x(h.uncompressed_size))
//fmt.Printf("FileName Length: %v\n", u16x(h.filename_length))
//fmt.Printf("ExtraField Length: %v\n", u16x(h.extra_field_length))
//fmt.Printf("ExtraField: %0 X\n", h.extra_field)
//if u16x(h.general_purpose_bit_flag) & 8 > 0 {
// crc32 := z.b.IGet(4)
// if u32x(crc32) == 0x08074b50 {
// // Signature may or may not be there
// crc32 = z.b.IGet(4)
// }
// csize := z.b.IGet(4)
// usize := z.b.IGet(4)
// fmt.Printf("DataDescriptor:\nCRC32: %0 X, Compressed: %v, Uncompressed: %v, Next Signature: %X\n", crc32, u16x(csize), u16x(usize), z.b.IGet(4))
//}
//
//crcbuf := make([]byte, 4)
//crcbuf[3] = h.crc_32[3]
//crcbuf[2] = (h.crc_32[3] >> 8)
//crcbuf[1] = (h.crc_32[3] >> 16)
//crcbuf[0] = (h.crc_32[3] >> 24)
//
////fmt.Printf("CRC BUF: %x %v\n", crcbuf, crcbuf)
//
////fmt.Printf("% X\n", file_data)
//if u16x(h.general_purpose_bit_flag) & 1 > 0 {
// fmt.Println("Encrypted")
// zc := zip2.NewZipCrypto(z.password)
//
// //result := file_data[0]
// //for i := 0; i < 12; i++ {
// // if i == 11 {
// // //fmt.Printf("HASH MAGIC: %X %v\n", result ^ zc.MagicByte(), result ^ zc.MagicByte())
// // }
// // zc.UpdateKeys(result ^ zc.MagicByte())
// // if i < 12 {
// // result = file_data[i + 1]
// // }
// //}
// file_header := zc.DecryptMessage(file_data[0:12])
// //fmt.Printf("Magic Byte: %X\n", zc.MagicByte())
// file_data = zc.DecryptMessage(file_data[12:])
// fmt.Printf("%0 X : %0 X\n", file_header, file_data)
// //fmt.Printf("%v %v\n", string(file_data[0:12]), string(file_data[12:]))
// //file_data = file_data[12:]
//}
//if u16x(h.compression_method) == 8 {
// //fmt.Printf("Compressed: %v\n", len(file_data))
// rc := flate.NewReader(bytes.NewBuffer(file_data))
// defer rc.Close()
// res, err := ioutil.ReadAll(rc)
// file_data = res
// if err != nil {
// fmt.Println(err.Error())
// }
//}
//crc := crc32.NewIEEE()
//crc.Write(file_data)
//fmt.Printf("GO CRC: %X\n", crc.Sum32())
//fmt.Printf("\n%v\n", string(file_data))
//}
}
func (z *ZipReader) DirectoryStructure() {
}
func u16(b []byte) uint16 {
return binary.LittleEndian.Uint16(b)
}
func u32(b []byte) uint32 {
return binary.LittleEndian.Uint32(b)
}
func u16x(b []byte) uint64 {
return uint64(u16(b))
}
func u32x(b []byte) uint64 {
return uint64(u32(b))
}
func u64x(b []byte) uint64 {
return uint64(u32(b))
}
type Bytes struct {
b []byte
i uint64
}
// Get slice of byte
func (b *Bytes) Get(offset, size uint64) []byte {
return b.b[offset : offset + size]
}
func (b *Bytes) BGet(offset, size uint64) *Bytes {
return &Bytes{b.Get(offset, size), 0}
}
// Incremental Get
func (b *Bytes) IGet(size uint64) []byte {
i := b.i
b.i = i + size
return b.b[i : b.i]
}
// Incremental Get
func (b *Bytes) IBGet(size uint64) *Bytes {
return &Bytes{b.IGet(size), 0}
}
func (b *Bytes) I64() uint64 {
switch len(b.b) {
case 1:
return uint64(b.b[0])
case 2:
return u16x(b.b)
case 4:
return u32x(b.b)
case 8:
return u64x(b.b)
}
return 0
}
// Set offset
func (b *Bytes) Offset(i uint64) {
b.i = i
}
func (b *Bytes) Pos() uint64 {
return b.i
}

View File

@ -11,10 +11,10 @@ import (
"hash" "hash"
"hash/crc32" "hash/crc32"
"io" "io"
"fmt"
) )
// TODO(adg): support zip file comments // TODO(adg): support zip file comments
// TODO(adg): support specifying deflate level
// Writer implements a zip file writer. // Writer implements a zip file writer.
type Writer struct { type Writer struct {
@ -22,6 +22,7 @@ type Writer struct {
dir []*header dir []*header
last *fileWriter last *fileWriter
closed bool closed bool
compressors map[uint16]Compressor
} }
type header struct { type header struct {
@ -78,7 +79,8 @@ func (w *Writer) Close() error {
b.uint16(h.ModifiedTime) b.uint16(h.ModifiedTime)
b.uint16(h.ModifiedDate) b.uint16(h.ModifiedDate)
b.uint32(h.CRC32) b.uint32(h.CRC32)
if h.isZip64() || h.offset > uint32max {
if h.isZip64() || h.offset >= uint32max {
// the file needs a zip64 header. store maxint in both // the file needs a zip64 header. store maxint in both
// 32 bit size fields (and offset later) to signal that the // 32 bit size fields (and offset later) to signal that the
// zip64 extra header should be used. // zip64 extra header should be used.
@ -98,6 +100,7 @@ func (w *Writer) Close() error {
b.uint32(h.CompressedSize) b.uint32(h.CompressedSize)
b.uint32(h.UncompressedSize) b.uint32(h.UncompressedSize)
} }
b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra))) b.uint16(uint16(len(h.Extra)))
b.uint16(uint16(len(h.Comment))) b.uint16(uint16(len(h.Comment)))
@ -211,8 +214,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
} }
fh.Flags |= 0x8 // we will write a data descriptor fh.Flags |= 0x8 // we will write a data descriptor
// TODO(alex): Look at spec and see if these need to be changed
// when using encryption.
fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
fh.ReaderVersion = zipVersion20 fh.ReaderVersion = zipVersion20
@ -221,30 +223,21 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
compCount: &countWriter{w: w.cw}, compCount: &countWriter{w: w.cw},
crc32: crc32.NewIEEE(), crc32: crc32.NewIEEE(),
} }
// Get the compressor before possibly changing Method to 99 due to password comp := w.compressor(fh.Method)
comp := compressor(fh.Method)
if comp == nil { if comp == nil {
return nil, ErrAlgorithm return nil, ErrAlgorithm
} }
// check for password // check for password
var sw io.Writer = fw.compCount var sw io.Writer = fw.compCount
if fh.password != nil { if fh.password != nil {
if fh.encryption == StandardEncryption { if !fh.IsEncrypted() {
fh.setEncryptionBit()
}
ew, err := ZipCryptoEncryptor(sw, fh.password, fw) ew, err := ZipCryptoEncryptor(sw, fh.password, fw)
if err != nil { if err != nil {
return nil, err return nil, err
} }
sw = ew sw = ew
} else {
// we have a password and need to encrypt.
fh.writeWinZipExtra()
fh.Method = 99 // ok to change, we've gotten the comp and wrote extra
ew, err := newEncryptionWriter(sw, fh.password, fw, fh.aesStrength)
if err != nil {
return nil, err
}
sw = ew
}
} }
var err error var err error
fw.comp, err = comp(sw) fw.comp, err = comp(sw)
@ -280,6 +273,7 @@ func writeHeader(w io.Writer, h *FileHeader) error {
b.uint32(0) // since we are writing a data descriptor crc32, b.uint32(0) // since we are writing a data descriptor crc32,
b.uint32(0) // compressed size, b.uint32(0) // compressed size,
b.uint32(0) // and uncompressed size should be zero b.uint32(0) // and uncompressed size should be zero
fmt.Printf("File HEader Wring %0 X, %v, %v\n", h.CRC32, h.CompressedSize, h.UncompressedSize)
b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra))) b.uint16(uint16(len(h.Extra)))
if _, err := w.Write(buf[:]); err != nil { if _, err := w.Write(buf[:]); err != nil {
@ -292,6 +286,24 @@ func writeHeader(w io.Writer, h *FileHeader) error {
return err return err
} }
// RegisterCompressor registers or overrides a custom compressor for a specific
// method ID. If a compressor for a given method is not found, Writer will
// default to looking up the compressor at the package level.
func (w *Writer) RegisterCompressor(method uint16, comp Compressor) {
if w.compressors == nil {
w.compressors = make(map[uint16]Compressor)
}
w.compressors[method] = comp
}
func (w *Writer) compressor(method uint16) Compressor {
comp := w.compressors[method]
if comp == nil {
comp = compressor(method)
}
return comp
}
type fileWriter struct { type fileWriter struct {
*header *header
zipw io.Writer zipw io.Writer
@ -320,21 +332,10 @@ func (w *fileWriter) close() error {
if err := w.comp.Close(); err != nil { if err := w.comp.Close(); err != nil {
return err return err
} }
// if encrypted grab the hmac and write it out
if w.header.IsEncrypted() && w.header.encryption != StandardEncryption {
authCode := w.hmac.Sum(nil)
authCode = authCode[:10]
_, err := w.compCount.Write(authCode)
if err != nil {
return errors.New("zip: error writing authcode")
}
}
// update FileHeader // update FileHeader
fh := w.header.FileHeader fh := w.header.FileHeader
// ae-2 we don't write out CRC
if !fh.IsEncrypted() || fh.encryption == StandardEncryption {
fh.CRC32 = w.crc32.Sum32() fh.CRC32 = w.crc32.Sum32()
}
fh.CompressedSize64 = uint64(w.compCount.count) fh.CompressedSize64 = uint64(w.compCount.count)
fh.UncompressedSize64 = uint64(w.rawCount.count) fh.UncompressedSize64 = uint64(w.rawCount.count)

View File

@ -3,6 +3,7 @@ package zip
import ( import (
"io" "io"
"bytes" "bytes"
"fmt"
"hash/crc32" "hash/crc32"
) )
@ -14,65 +15,74 @@ type ZipCrypto struct {
func NewZipCrypto(passphrase []byte) *ZipCrypto { func NewZipCrypto(passphrase []byte) *ZipCrypto {
z := &ZipCrypto{} z := &ZipCrypto{}
z.password = passphrase z.password = passphrase
z.init() z.Init()
return z return z
} }
func (z *ZipCrypto) init() { func (z *ZipCrypto) Init() {
z.Keys[0] = 0x12345678 z.Keys[0] = 0x12345678
z.Keys[1] = 0x23456789 z.Keys[1] = 0x23456789
z.Keys[2] = 0x34567890 z.Keys[2] = 0x34567890
for i := 0; i < len(z.password); i++ { for i := 0; i < len(z.password); i++ {
z.updateKeys(z.password[i]) z.UpdateKeys(z.password[i])
} }
} }
func (z *ZipCrypto) updateKeys(byteValue byte) { func (z *ZipCrypto) UpdateKeys(byteValue byte) {
z.Keys[0] = crc32update(z.Keys[0], byteValue); z.Keys[0] = Crc32update(z.Keys[0], byteValue);
z.Keys[1] += z.Keys[0] & 0xff; z.Keys[1] += z.Keys[0] & 0xff;
z.Keys[1] = z.Keys[1] * 134775813 + 1; z.Keys[1] = z.Keys[1] * 134775813 + 1;
z.Keys[2] = crc32update(z.Keys[2], (byte) (z.Keys[1] >> 24)); z.Keys[2] = Crc32update(z.Keys[2], (byte) (z.Keys[1] >> 24));
} }
func (z *ZipCrypto) magicByte() byte { func (z *ZipCrypto) MagicByte() byte {
var t uint32 = z.Keys[2] | 2 var t uint32 = z.Keys[2] | 2
return byte((t * (t ^ 1)) >> 8) return byte((t * (t ^ 1)) >> 8)
} }
func (z *ZipCrypto) Encrypt(data []byte) []byte { func (z *ZipCrypto) EncryptMessage(plaintext []byte) []byte {
length := len(data) length := len(plaintext)
chiper := make([]byte, length) CipherText := make([]byte, length)
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
v := data[i] C := plaintext[i]
chiper[i] = v ^ z.magicByte() CipherText[i] = plaintext[i] ^ z.MagicByte()
z.updateKeys(v) z.UpdateKeys(C)
} }
return chiper return CipherText
} }
func (z *ZipCrypto) Decrypt(chiper []byte) []byte { func (z *ZipCrypto) DecryptMessage(chipertext []byte) []byte {
length := len(chiper) length := len(chipertext)
plain := make([]byte, length) PlainText := make([]byte, length)
for i, c := range chiper { for i, c := range chipertext {
v := c ^ z.magicByte(); v := c ^ z.MagicByte();
z.updateKeys(v) z.UpdateKeys(v)
plain[i] = v PlainText[i] = v
} }
return plain return PlainText
} }
func crc32update(pCrc32 uint32, bval byte) uint32 { func Crc32update(pCrc32 uint32, bval byte) uint32 {
return crc32.IEEETable[(pCrc32 ^ uint32(bval)) & 0xff] ^ (pCrc32 >> 8) return crc32.IEEETable[(pCrc32 ^ uint32(bval)) & 0xff] ^ (pCrc32 >> 8)
} }
func ZipCryptoDecryptor(r *io.SectionReader, password []byte) (*io.SectionReader, error) { func ZipCryptoDecryptor(r *io.SectionReader, password []byte) (*io.SectionReader, error) {
//return r, nil
z := NewZipCrypto(password) z := NewZipCrypto(password)
b := make([]byte, r.Size()) b := make([]byte, r.Size())
r.Read(b) r.Read(b)
m := z.Decrypt(b) m := z.DecryptMessage(b)
fmt.Printf("Header: %X %X %X\n%v\n", m[0:4], m[4:8], m[8:12], m[0:12])
fmt.Printf("Header: %X\n", m[11])
fmt.Printf("Content: %X\n", m[12:])
fmt.Printf("Content: %v\n", string(m[12:]))
//if m[11] > 0xFF {
// return nil, errors.New("incorrect password")
//}
//fmt.Printf("Size: %v\n", len(m))
return io.NewSectionReader(bytes.NewReader(m), 12, int64(len(m))), nil return io.NewSectionReader(bytes.NewReader(m), 12, int64(len(m))), nil
} }
@ -84,25 +94,27 @@ type zipCryptoWriter struct {
} }
func (z *zipCryptoWriter) Write(p []byte) (n int, err error) { func (z *zipCryptoWriter) Write(p []byte) (n int, err error) {
err = nil
if z.first { if z.first {
z.first = false z.first = false
header := []byte{0xF8, 0x53, 0xCF, 0x05, 0x2D, 0xDD, 0xAD, 0xC8, 0x66, 0x3F, 0x8C, 0xAC} header := []byte{0xF8, 0x53, 0xCF, 0x05, 0x2D, 0xDD, 0xAD, 0xC8, 0x66, 0x3F, 0x8C, 0xAC}
header = z.z.Encrypt(header) header = z.z.EncryptMessage(header)
crc := z.fw.ModifiedTime crc := z.fw.ModifiedTime
header[10] = byte(crc) header[10] = byte(crc)
header[11] = byte(crc >> 8) header[11] = byte(crc >> 8)
z.z.init() z.z.Init()
z.w.Write(z.z.Encrypt(header)) z.w.Write(z.z.EncryptMessage(header))
n += 12 n += 12
//z.z.Init()
} }
z.w.Write(z.z.Encrypt(p)) z.w.Write(z.z.EncryptMessage(p))
z.fw.FileHeader.CompressedSize += uint32(n)
return return
} }
func ZipCryptoEncryptor(i io.Writer, pass passwordFn, fw *fileWriter) (io.Writer, error) { func ZipCryptoEncryptor(i io.Writer, pass passwordFn, fw *fileWriter) (io.Writer, error) {
fmt.Printf("Initializationg...\n")
z := NewZipCrypto(pass()) z := NewZipCrypto(pass())
zc := &zipCryptoWriter{i, z, true, fw} zc := &zipCryptoWriter{i, z, true, fw}
return zc, nil return zc, nil