Compare commits

..

13 Commits

Author SHA1 Message Date
re 5be397991f fix repos 2022-12-13 19:19:31 +03:00
Yakub Kristianto d046722c6f
Create LICENSE 2018-09-14 19:55:37 +07:00
Yakub Kristianto 77ea4e565c
Merge pull request #3 from roberthdevries/master
Fix typo in import statement
2018-06-25 12:16:09 +07:00
Robert de Vries b5e29b11a1
Fix typo in import statement 2018-03-25 12:40:52 +02:00
Yakub Kristianto a58f9a5124
Merge pull request #2 from roberthdevries/master
Add missing "io" import to README example
2018-03-21 20:34:41 +07:00
Robert de Vries 736667b903 Add missing "io" import to README example 2018-03-18 22:44:39 +01:00
yeka 6c433e67d3 Change README to include example 2018-02-23 12:34:12 +07:00
yeka d851e4060f Add encryption method in test 2018-02-23 12:14:14 +07:00
yeka f2052480f0 Write CRC for Standard Zip Encryption 2018-02-23 12:14:14 +07:00
yeka 673b7b98d0 Add encrypt & decrypt using zip standard encryption 2018-02-23 12:14:14 +07:00
yeka feedcee201 Selectable Encryption Method 2018-02-23 12:14:14 +07:00
Alex Mullins f15f84f02c
Merge pull request #1 from farmerx/patch-1
modify readme.md add example
2017-11-16 23:59:17 -06:00
Farmerx b2c4533ffb
完善一下readme.md
Hi! alexmullins:
     在我使用你这个包的时候,着了所有的例子,都没有成功。 最后当我看你你的这个zip fork golang zip 。我恍然大悟,原来你的这个包是这么用的。为了以后更多的人使用你的这个zip包,请接收我的提交
2017-11-16 20:38:45 -06:00
12 changed files with 268 additions and 785 deletions

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
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.

92
README.md Normal file
View File

@ -0,0 +1,92 @@
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))
}
}
```

View File

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

View File

@ -175,9 +175,55 @@ func TestPasswordWriteSimple(t *testing.T) {
contents := []byte("Hello World")
conLen := len(contents)
for _, enc := range []EncryptionMethod{StandardEncryption, AES128Encryption, AES192Encryption, AES256Encryption} {
raw := new(bytes.Buffer)
zipw := NewWriter(raw)
w, err := zipw.Encrypt("hello.txt", "golang", enc)
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()
// Read the zip
buf := new(bytes.Buffer)
zipr, err := NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len()))
if err != nil {
t.Errorf("Expected to open a new zip reader: %v", err)
}
nn := len(zipr.File)
if nn != 1 {
t.Errorf("Expected to have one file in the zip archive, but has %d files", nn)
}
z := zipr.File[0]
z.SetPassword("golang")
rr, err := z.Open()
if err != nil {
t.Errorf("Expected to open the readcloser: %v", err)
}
n, err = io.Copy(buf, rr)
if err != nil {
t.Errorf("Expected to write to temporary buffer: %v", err)
}
if n != int64(conLen) {
t.Errorf("Expected to copy %d bytes to temp buffer, but copied %d bytes instead", conLen, n)
}
if !bytes.Equal(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")
w, err := zipw.Encrypt("hello.txt", "golang", StandardEncryption)
if err != nil {
t.Errorf("Expected to create a new FileHeader")
}
@ -187,30 +233,14 @@ func TestPasswordWriteSimple(t *testing.T) {
}
zipw.Close()
// Read the zip
buf := new(bytes.Buffer)
zipr, err := NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len()))
if err != nil {
t.Errorf("Expected to open a new zip reader: %v", err)
}
nn := len(zipr.File)
if nn != 1 {
t.Errorf("Expected to have one file in the zip archive, but has %d files", nn)
}
z := zipr.File[0]
z.SetPassword("golang")
rr, err := z.Open()
if err != nil {
t.Errorf("Expected to open the readcloser: %v", err)
}
n, err = io.Copy(buf, rr)
if err != nil {
t.Errorf("Expected to write to temporary buffer: %v", err)
}
if n != int64(conLen) {
t.Errorf("Expected to copy %d bytes to temp buffer, but copied %d bytes instead", conLen, n)
}
if !bytes.Equal(contents, buf.Bytes()) {
t.Errorf("Expected the unzipped contents to equal '%s', but was '%s' instead", contents, buf.Bytes())
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"
"os"
"github.com/alexmullins/zip"
"git.internal/re/zip"
)
func ExampleWriter() {
@ -22,7 +22,7 @@ func ExampleWriter() {
w := zip.NewWriter(buf)
// Add some files to the archive.
var files = []struct {
files := []struct {
Name, Body string
}{
{"readme.txt", "This archive contains some text files."},
@ -81,7 +81,7 @@ func ExampleWriter_Encrypt() {
// write a password zip
raw := new(bytes.Buffer)
zipw := zip.NewWriter(raw)
w, err := zipw.Encrypt("hello.txt", "golang")
w, err := zipw.Encrypt("hello.txt", "golang", zip.AES256Encryption)
if err != nil {
log.Fatal(err)
}

View File

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

View File

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

View File

@ -1,32 +0,0 @@
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)
}

View File

@ -1,544 +0,0 @@
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,18 +11,17 @@ import (
"hash"
"hash/crc32"
"io"
"fmt"
)
// TODO(adg): support zip file comments
// TODO(adg): support specifying deflate level
// Writer implements a zip file writer.
type Writer struct {
cw *countWriter
dir []*header
last *fileWriter
closed bool
compressors map[uint16]Compressor
cw *countWriter
dir []*header
last *fileWriter
closed bool
}
type header struct {
@ -53,7 +52,7 @@ func (w *Writer) Flush() error {
}
// Close finishes writing the zip file by writing the central directory.
// It does not (and cannot) close the underlying writer.
// It does not (and can not) close the underlying writer.
func (w *Writer) Close() error {
if w.last != nil && !w.last.closed {
if err := w.last.close(); err != nil {
@ -79,8 +78,7 @@ func (w *Writer) Close() error {
b.uint16(h.ModifiedTime)
b.uint16(h.ModifiedDate)
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
// 32 bit size fields (and offset later) to signal that the
// zip64 extra header should be used.
@ -100,7 +98,6 @@ func (w *Writer) Close() error {
b.uint32(h.CompressedSize)
b.uint32(h.UncompressedSize)
}
b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra)))
b.uint16(uint16(len(h.Comment)))
@ -214,7 +211,8 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
}
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.ReaderVersion = zipVersion20
@ -223,21 +221,30 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
compCount: &countWriter{w: w.cw},
crc32: crc32.NewIEEE(),
}
comp := w.compressor(fh.Method)
// Get the compressor before possibly changing Method to 99 due to password
comp := compressor(fh.Method)
if comp == nil {
return nil, ErrAlgorithm
}
// check for password
var sw io.Writer = fw.compCount
if fh.password != nil {
if !fh.IsEncrypted() {
fh.setEncryptionBit()
if fh.encryption == StandardEncryption {
ew, err := ZipCryptoEncryptor(sw, fh.password, fw)
if err != nil {
return nil, err
}
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
}
ew, err := ZipCryptoEncryptor(sw, fh.password, fw)
if err != nil {
return nil, err
}
sw = ew
}
var err error
fw.comp, err = comp(sw)
@ -273,7 +280,6 @@ func writeHeader(w io.Writer, h *FileHeader) error {
b.uint32(0) // since we are writing a data descriptor crc32,
b.uint32(0) // compressed size,
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.Extra)))
if _, err := w.Write(buf[:]); err != nil {
@ -286,24 +292,6 @@ func writeHeader(w io.Writer, h *FileHeader) error {
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 {
*header
zipw io.Writer
@ -332,10 +320,21 @@ func (w *fileWriter) close() error {
if err := w.comp.Close(); err != nil {
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
fh := w.header.FileHeader
fh.CRC32 = w.crc32.Sum32()
// ae-2 we don't write out CRC
if !fh.IsEncrypted() || fh.encryption == StandardEncryption {
fh.CRC32 = w.crc32.Sum32()
}
fh.CompressedSize64 = uint64(w.compCount.count)
fh.UncompressedSize64 = uint64(w.rawCount.count)

View File

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